编码流程
- 注册所有组件 av_register_all()
- 创建封装格式上下文 avformat_alloc_context() 返回一个AVFormatContext
- 初始化输入输出上下文(打开输出文件) avio_open(),可以输出到本地或者网络地址等等
- 创建媒体流 avformat_new_stream()
- 初始化编码器上下文以及设置参数 AVCodecContext
- 查找编码器 avcodec_find_encoder_by_name()
- 打开编码器 avcodec_open2()
- 写入头文件 avformat_write_header();
- 打开本地需要转换的文件 fopen(inFile, “rb”)
- 初始化 AVFrame,AVPacket
- 开始转换 avcodec_send_frame() avcodec_receive_packet()
- 开始写入本地 av_write_frame()
- 写入文件尾 av_write_trailer()
公共变量
AVCodec *avAudioCodec; //编码器对象
AVCodecContext *avAudioContext; //编码器上下文
AVFrame *audioFrame; //储存原始PCM数据AVFrame
AVPacket *audioPacket; //储存编码后AAC数据AVPacket
AVStream *audioStream; //流通道对象
int audioPts = 0; //pts
AVFormatContext *formatContext; //封装格式上下文对象
int audioBufferSize; //缓冲区大小
uint8_t *outBuffer; //缓冲区buffer
FILE *in_file; //本地音频文件
初始化
int AudioVideoEncode::audioEncoderInit(JNIEnv *env) {
//输出文件(AAC)路径
const char *outFile = "/sdcard/aaa/gjk.aac";
//原始文件(PCM)路径
const char *inFile = "/sdcard/aaa/PCM/pcm.pcm";
//注册组件
av_register_all();
//初始化封装格式上下文
formatContext = avformat_alloc_context();
//指定封装类型
formatContext->oformat = av_guess_format(NULL, outFile, NULL);//得到输出格式
//打开输出文件
if (avio_open(&formatContext->pb, outFile, AVIO_FLAG_READ_WRITE) < 0) return -1;
//创建媒体流
audioStream = avformat_new_stream(formatContext, 0);
if (audioStream == NULL) return -1;
//获取编码器上下文
avAudioContext = audioStream->codec;
//设置编码器上下文参数
avAudioContext->codec_id = formatContext->oformat->audio_codec;//设置id
avAudioContext->codec_type = AVMEDIA_TYPE_AUDIO;//设置类型为音频
avAudioContext->sample_fmt = AV_SAMPLE_FMT_S16;//设置格式为16bit
avAudioContext->sample_rate = 8000;//设置采样率
avAudioContext->channel_layout = AV_CH_LAYOUT_MONO;//设置通道类型和下面通道数量对应
avAudioContext->channels = 1;//单通道
avAudioContext->bit_rate = 128000;//码率
//查找编码器
avAudioCodec = avcodec_find_encoder_by_name("libfdk_aac");
if (avAudioCodec == NULL) return -1;
//打开编码器
int ret = avcodec_open2(avAudioContext, avAudioCodec, NULL);
if (ret < 0) return -1;
//写入头文件
avformat_write_header(formatContext, NULL);
//初始化编码前(原始数据)数据储存结构体,也就是创建数据缓冲区
audioFrame = av_frame_alloc();
//设置参数
audioFrame->nb_samples = avAudioContext->frame_size;
audioFrame->format = avAudioContext->sample_fmt;
//打开音频输入文件
in_file = fopen(inFile, "rb");
if (in_file == NULL) return -1;
//获取缓冲区大小
audioBufferSize = av_samples_get_buffer_size(NULL,
avAudioContext->channels,
avAudioContext->frame_size,
avAudioContext->sample_fmt,
1);
//创建缓冲区
outBuffer = (uint8_t *) av_malloc(audioBufferSize);
avcodec_fill_audio_frame(audioFrame,
avAudioContext->channels,
avAudioContext->sample_fmt,
(const uint8_t *) outBuffer,
audioBufferSize,
1);
//初始化编码后数据储存结构体
audioPacket = (AVPacket *) av_malloc(audioBufferSize);
audioPts = 0;//pts
return 0;
}
开始编码
void AudioVideoEncode::audioEncoder(JNIEnv *env, jobject obj, jbyte *byte, int byteLength) {
int ret;
while (true) {
// 1、读取一帧音频采样数据
if (fread(outBuffer, 1, audioBufferSize, in_file) <= 0) {
__android_log_print(ANDROID_LOG_INFO, "main", "读取数据失败");
break;
} else if (feof(in_file)) {
break;
}
audioFrame->data[0] = outBuffer;
audioFrame->pts = audioPts;
audioPts++;
ret = avcodec_send_frame(avAudioContext, audioFrame);
if (ret != 0) {
__android_log_print(ANDROID_LOG_INFO, "main", "发送数据失败");
}
// 3.2 编码一帧音频采样数据
ret = avcodec_receive_packet(avAudioContext, audioPacket);
if (ret == 0) {
/**第十一步:将编码后的音频码流写入文件*/
audioPts++;
audioPacket->stream_index = audioStream->index;
ret = av_write_frame(formatContext, audioPacket);
if (ret < 0) {
__android_log_print(ANDROID_LOG_INFO, "main", "写入失败");
return;
}
} else {
__android_log_print(ANDROID_LOG_INFO, "main", "无采样数据编码");
return;
}
}
// 写文件尾(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)
av_write_trailer(formatContext);
//释放资源
videoEncoderClose();
}
版权声明:本文为aagjkgluhgyy原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。