应用到的API及解释
avcodec_register_all:注册FFmpeg所有编解码器。
avformat_alloc_output_context2():初始化输出码流的AVFormatContext。
avio_open():打开输出文件。
av_new_stream():创建输出码流的AVStream。
avcodec_find_encoder():查找编码器。
avcodec_open2():打开编码器。
avformat_write_header():写文件头(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。
avcodec_encode_audio2():编码音频。即将AVFrame(存储PCM采样数据)编码为AVPacket(存储AAC,MP3等格式的码流数据)。
av_write_frame():将编码后的视频码流写入文件。
av_write_trailer():写文件尾(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)。
源代码如下:
#include <stdlib.h>
#include <stdio.h>
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libavutil/log.h"
#include "libswresample/swresample.h"
AVFormatContext* inputContext;
AVFormatContext* outputContext;
AVCodecContext* decodeContext;
AVCodecContext* encodeContext;
AVStream* stream;
void init(){
avcodec_register_all();
}
int open_output_context(char* filename){
outputContext = avformat_alloc_context();
int ret = avformat_alloc_output_context2(&outputContext,NULL,NULL,filename);
if(ret < 0){
av_log(NULL,AV_LOG_ERROR,"create outputContext failed \n");
return -1;
}else{
av_log(NULL,AV_LOG_INFO,"create outputContext success \n");
}
ret = avio_open(&outputContext->pb,filename,AVIO_FLAG_READ_WRITE);
if(ret < 0){
av_log(NULL,AV_LOG_ERROR,"open file failed \n");
return -1;
}else{
av_log(NULL,AV_LOG_INFO,"open file success \n");
}
stream = avformat_new_stream(outputContext,NULL);
if(!stream){
av_log(NULL,AV_LOG_ERROR,"avformat_new_stream failed \n");
return -1;
}else{
av_log(NULL,AV_LOG_INFO,"avformat_new_stream success \n");
}
av_dump_format(outputContext,0,filename,1);
return ret;
}
void close(){
if(decodeContext){
avcodec_close(decodeContext);
}
if(inputContext){
avformat_close_input(&inputContext);
}
if(outputContext){
for(int i = 0 ; i < outputContext->nb_streams; i++){
AVCodecContext* codecContext = outputContext->streams[i]->codec;
avcodec_close(codecContext);
}
avformat_close_input(&outputContext);
}
}
int init_encode(AVStream* audio_stream){
encodeContext = audio_stream->codec;
encodeContext->codec = outputContext->audio_codec;
encodeContext->codec_id = outputContext->audio_codec_id;
encodeContext->codec_type = AVMEDIA_TYPE_AUDIO;
encodeContext->sample_fmt = AV_SAMPLE_FMT_S16;
encodeContext->sample_rate = 44100;
encodeContext->channel_layout=AV_CH_LAYOUT_STEREO;
encodeContext->channels = av_get_channel_layout_nb_channels(encodeContext->channel_layout);
encodeContext->bit_rate = 64000;
AVCodec* pCodec = avcodec_find_encoder_by_name("libfdk_aac");
if(pCodec){
av_log(NULL,AV_LOG_INFO,"avcodec_find_encoder success\n");
}else{
av_log(NULL,AV_LOG_ERROR,"avcodec_find_encoder failed\n");
return -1;
}
return avcodec_open2(encodeContext,pCodec,NULL);
}
static void encode(AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt)
{
int ret;
if (frame)
printf("Send frame %3"PRId64"\n", frame->pts);
ret = avcodec_send_frame(enc_ctx, frame);
if (ret < 0) {
fprintf(stderr, "Error sending a frame for encoding\n");
exit(1);
}
while (ret >= 0) {
ret = avcodec_receive_packet(enc_ctx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error during encoding\n");
exit(1);
}
printf("Write packet %3"PRId64" (size=%5d)\n", pkt->pts, pkt->size);
av_write_frame(outputContext,pkt);
av_packet_unref(pkt);
}
}
int main(){
init();
char* inputFile = "jichi.pcm";
char* outputFile = "jichi2.aac";
FILE* in_file = fopen(inputFile,"rb");
if(in_file){
av_log(NULL,AV_LOG_INFO,"open file success \n");
}else{
av_log(NULL,AV_LOG_ERROR,"can't open file! \n");
goto _END;
}
int ret = open_output_context(outputFile);
if(ret >= 0){
av_log(NULL,AV_LOG_INFO,"open_output_context success \n");
}else{
av_log(NULL,AV_LOG_ERROR,"open_output_context failed! \n");
goto _END;
}
if (stream==NULL){
av_log(NULL,AV_LOG_ERROR,"avformat_new_stream failed \n");
goto _END;
}else{
av_log(NULL,AV_LOG_INFO,"avformat_new_stream success \n");
}
ret = init_encode(stream);
if(ret < 0){
av_log(NULL,AV_LOG_ERROR,"init_encoder failed \n");
goto _END;
}else{
av_log(NULL,AV_LOG_INFO,"init encoder success \n");
}
ret = avcodec_parameters_from_context(stream->codecpar, encodeContext);
if(ret < 0){
printf("avcodec_parameters_from_context failed \n");
}else{
printf("avcodec_parameters_from_context success \n");
}
AVFrame* pFrame = av_frame_alloc();
pFrame->nb_samples= encodeContext->frame_size;
pFrame->format= encodeContext->sample_fmt;
int size = av_samples_get_buffer_size(NULL, encodeContext->channels,encodeContext->frame_size,encodeContext->sample_fmt, 1);
int8_t* frame_buf = (uint8_t *)av_malloc(size);
avcodec_fill_audio_frame(pFrame, encodeContext->channels, encodeContext->sample_fmt,(const uint8_t*)frame_buf, size, 1);
avformat_write_header(outputContext,NULL);
AVPacket pkt;
av_new_packet(&pkt,size);
int got_frame;
int i = 0;
while(1){
if (fread(frame_buf, 1, size, in_file) <= 0){
printf("Failed to read raw data! \n");
break;
}else if(feof(in_file)){
break;
}
pFrame->data[0] = frame_buf;
pFrame->pts=i*100;
encode(encodeContext,pFrame,&pkt);
i++;
}
av_write_trailer(outputContext);
printf("编码完成\n");
AVCodec* encoder = avcodec_find_encoder(outputContext->oformat->audio_codec);
if(!encoder){
av_log(NULL,AV_LOG_ERROR,"can't find endcoder \n");
}else{
av_log(NULL,AV_LOG_INFO," find endcoder \n");
av_log(NULL,AV_LOG_ERROR,"encoder name = %s \n",encoder->name);
}
av_free(frame_buf);
_END:
av_frame_unref(pFrame);
if(in_file){
fclose(in_file);
}
close();
return 0;
}