文章目录
pcm和yuv编码为aac和h264,封装为c++的AudioEncoder类和VideoEncoder类
流程
音频
-
初始化音频参数
int InitAAC(int channels, int sample_rate, int bit_rate);
-
音频编码,pts需要转化为编码时的pts, 发送帧frame, 获取 packet
AVPacket *Encode(AVFrame *farme, int stream_index, int64_t pts, int64_t time_base);
-
获取一帧数据通道号的采样点
int GetFrameSize()
-
获取编码器需要的采样格式
int GetSampleFormat()
-
释放资源
void DeInit();
视频
-
初始化视频参数
int InitH264(int width, int height, int fps, int bit_rate);
-
视频编码,pts需要转化为编码时的pts, 需要初始化AVFrame结构体,发送帧frame, 获取 packet
AVPacket *Encode(AVFrame *farme, int stream_index, int64_t pts, int64_t time_base);
-
释放资源
void DeInit();
api
- 用于初始化一个AVFrame结构体,包括设置AVFrame中的各项参数,不需要重新分配buffer,需要传入一个指向AVFrame结构体的指针
cpp
int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4],
const uint8_t *src,
enum AVPixelFormat pix_fmt, int width, int height, int align);
核心代码
audioencoder.h
cpp
#ifndef AUDIOENCODER_H
#define AUDIOENCODER_H
extern "C"
{
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
}
class AudioEncoder
{
public:
AudioEncoder();
~AudioEncoder();
int InitAAC(int channels, int sample_rate, int bit_rate);
// int InitMP3(/*int channels, int sample_rate, int bit_rate*/);
void DeInit(); // 释放资源
AVPacket *Encode(AVFrame *farme, int stream_index, int64_t pts, int64_t time_base);
int GetFrameSize(); // 获取一帧数据 每个通道需要多少个采样点
int GetSampleFormat(); // 编码器需要的采样格式
private:
int channels_ = 2;
int sample_rate_ = 44100;
int bit_rate_ = 128*1024;
int64_t pts_ = 0;
AVCodecContext * codec_ctx_ = NULL;
};
#endif // AUDIOENCODER_H
audioencoder.cpp
cpp
#include "audioencoder.h"
AudioEncoder::AudioEncoder()
{
}
AudioEncoder::~AudioEncoder()
{
if(codec_ctx_) {
DeInit();
}
}
int AudioEncoder::InitAAC(int channels, int sample_rate, int bit_rate)
{
channels_ = channels;
sample_rate_ = sample_rate;
bit_rate_ = bit_rate;
AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
if(!codec) {
printf("avcodec_find_encoder AV_CODEC_ID_AAC failed\n");
return -1;
}
codec_ctx_ = avcodec_alloc_context3(codec);
if(!codec_ctx_) {
printf("avcodec_alloc_context3 AV_CODEC_ID_AAC failed\n");
return -1;
}
codec_ctx_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
codec_ctx_->bit_rate = bit_rate_;
codec_ctx_->sample_rate = sample_rate_;
codec_ctx_->sample_fmt = AV_SAMPLE_FMT_FLTP;
codec_ctx_->channels = channels_;
codec_ctx_->channel_layout = av_get_default_channel_layout(codec_ctx_->channels);
int ret = avcodec_open2(codec_ctx_, NULL, NULL);
if(ret != 0) {
char errbuf[1024] = {0};
av_strerror(ret, errbuf, sizeof(errbuf) - 1);
printf("avcodec_open2 failed:%s\n", errbuf);
return -1;
}
printf("InitAAC success\n");
return 0;
}
void AudioEncoder::DeInit()
{
if(codec_ctx_) {
avcodec_free_context(&codec_ctx_); // codec_ctx_被设置为NULL
// codec_ctx_ = NULL; // 不需要再写
}
}
AVPacket *AudioEncoder::Encode(AVFrame *frame, int stream_index, int64_t pts, int64_t time_base)
{
if(!codec_ctx_) {
printf("codec_ctx_ null\n");
return NULL;
}
pts = av_rescale_q(pts, AVRational{1, (int)time_base}, codec_ctx_->time_base);
if(frame) {
frame->pts = pts;
}
int ret = avcodec_send_frame(codec_ctx_, frame);
if(ret != 0) {
char errbuf[1024] = {0};
av_strerror(ret, errbuf, sizeof(errbuf) - 1);
printf("avcodec_send_frame failed:%s\n", errbuf);
return NULL;
}
AVPacket *packet = av_packet_alloc();
ret = avcodec_receive_packet(codec_ctx_, packet);
if(ret != 0) {
char errbuf[1024] = {0};
av_strerror(ret, errbuf, sizeof(errbuf) - 1);
printf("avcodec_receive_packet failed:%s\n", errbuf);
av_packet_free(&packet);
return NULL;
}
packet->stream_index = stream_index;
return packet;
}
int AudioEncoder::GetFrameSize()
{
if(codec_ctx_)
return codec_ctx_->frame_size;
return 0;
}
int AudioEncoder::GetSampleFormat()
{
if(codec_ctx_)
return codec_ctx_->sample_fmt;
return -1; // AV_SAMPLE_FMT_NONE
}
videoencoder.h
cpp
#ifndef VIDEOENCODER_H
#define VIDEOENCODER_H
extern "C"
{
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
}
class VideoEncoder
{
public:
VideoEncoder();
~VideoEncoder();
int InitH264(int width, int height, int fps, int bit_rate);
void DeInit();
AVPacket *Encode(uint8_t *yuv_data, int yuv_size,
int stream_index, int64_t pts, int64_t time_base);
private:
int width_ = 0;
int height_ = 0;
int fps_ = 25;
int bit_rate_ = 500*1024;
int64_t pts_ = 0;
AVCodecContext * codec_ctx_ = NULL;
AVFrame *frame_ = NULL;
};
#endif // VIDEOENCODER_H
videoencoder.cpp
cpp
#include "videoencoder.h"
extern "C"
{
#include "libavutil/imgutils.h"
}
VideoEncoder::VideoEncoder()
{
}
VideoEncoder::~VideoEncoder()
{
if(codec_ctx_) {
DeInit();
}
}
int VideoEncoder::InitH264(int width, int height, int fps, int bit_rate)
{
width_ = width;
height_ = height;
fps_ = fps;
bit_rate_ = bit_rate;
AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if(!codec) {
printf("avcodec_find_encoder AV_CODEC_ID_H264 failed\n");
return -1;
}
codec_ctx_ = avcodec_alloc_context3(codec);
if(!codec_ctx_) {
printf("avcodec_alloc_context3 AV_CODEC_ID_H264 failed\n");
return -1;
}
codec_ctx_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
codec_ctx_->bit_rate = bit_rate_;
codec_ctx_->width = width_;
codec_ctx_->height = height_;
codec_ctx_->framerate = {fps_, 1};
codec_ctx_->time_base = {1, 1000000}; // 单位为微妙
codec_ctx_->gop_size = fps_;
codec_ctx_->max_b_frames = 0;
codec_ctx_->pix_fmt = AV_PIX_FMT_YUV420P;
int ret = avcodec_open2(codec_ctx_, NULL, NULL);
if(ret != 0) {
char errbuf[1024] = {0};
av_strerror(ret, errbuf, sizeof(errbuf) - 1);
printf("avcodec_open2 failed:%s\n", errbuf);
return -1;
}
frame_ = av_frame_alloc();
if(!frame_) {
printf("av_frame_alloc failed\n");
return -1;
}
frame_->width = width_;
frame_->height = height_;
frame_->format = codec_ctx_->pix_fmt;
printf("Inith264 success\n");
return 0;
}
void VideoEncoder::DeInit()
{
if(codec_ctx_) {
avcodec_free_context(&codec_ctx_);
}
if(frame_) {
av_frame_free(&frame_);
}
}
AVPacket *VideoEncoder::Encode(uint8_t *yuv_data, int yuv_size, int stream_index, int64_t pts, int64_t time_base)
{
if(!codec_ctx_) {
printf("codec_ctx_ null\n");
return NULL;
}
int ret = 0;
pts = av_rescale_q(pts, AVRational{1, (int)time_base}, codec_ctx_->time_base);
frame_->pts = pts;
if(yuv_data) {
//该函数用于初始化一个AVFrame结构体,包括设置AVFrame中的各项参数,不需要重新分配buffer
//使用该函数需要传入一个指向AVFrame结构体的指针
int ret_size = av_image_fill_arrays(frame_->data, frame_->linesize,
yuv_data, (AVPixelFormat)frame_->format,
frame_->width, frame_->height, 1);
if(ret_size != yuv_size) {
printf("ret_size:%d != yuv_size:%d -> failed\n", ret_size, yuv_size);
return NULL;
}
ret = avcodec_send_frame(codec_ctx_, frame_);
} else {
ret = avcodec_send_frame(codec_ctx_, NULL);
}
if(ret != 0) {
char errbuf[1024] = {0};
av_strerror(ret, errbuf, sizeof(errbuf) - 1);
printf("avcodec_send_frame failed:%s\n", errbuf);
return NULL;
}
AVPacket *packet = av_packet_alloc();
ret = avcodec_receive_packet(codec_ctx_, packet);
if(ret != 0) {
char errbuf[1024] = {0};
av_strerror(ret, errbuf, sizeof(errbuf) - 1);
printf("avcodec_receive_packet failed:%s\n", errbuf);
av_packet_free(&packet);
return NULL;
}
packet->stream_index = stream_index;
return packet;
}