XCodec
作用:编码和解码的基类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #pragma once #include <mutex> #include <vector>
extern "C" { #include <libavcodec/avcodec.h> #include <libavutil/avutil.h> }
struct AVCodecContext; struct AVPacket; struct AVFrame;
class XCodec { public:
static AVCodecContext* Create(int codec_id, bool is_encode);
void set_c(AVCodecContext* c);
bool SetOpt(const char* key, const char* val); bool SetOpt(const char* key, int val);
bool Open();
AVFrame *CreateFrame();
protected: AVCodecContext* c_ = nullptr; std::mutex mux_; };
|
参数介绍
1 2 3
| protected: AVCodecContext* c_ = nullptr; std::mutex mux_;
|
XCodec.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
| #include "xcodec.h" #include <iostream> #include "xtools.h"
using namespace std;
extern "C" { #include <libavcodec/avcodec.h> #include <libavutil/opt.h> }
#pragma comment(lib, "avcodec.lib") #pragma comment(lib, "avutil.lib")
AVCodecContext* XCodec::Create(int codec_id, bool is_encode) { const AVCodec *codec = nullptr; if(is_encode) codec = avcodec_find_encoder((AVCodecID)codec_id); else codec = avcodec_find_decoder((AVCodecID)codec_id); if (!codec) { cerr << "avcodec_find_encoder failed! " << codec_id << endl; return nullptr; }
auto c = avcodec_alloc_context3(codec); if (!c) { cerr << "avcodec_alloc_context3 failed! " << codec_id << endl; return nullptr; }
c->time_base = { 1, 25 }; c->pix_fmt = AV_PIX_FMT_YUV420P; c->thread_count = 16; return c; }
void XCodec::set_c(AVCodecContext* c) { unique_lock<mutex> lock(mux_); if (c_) { avcodec_free_context(&c_); }
this->c_ = c; }
bool XCodec::SetOpt(const char* key, const char* val) { unique_lock<mutex> lock(mux_); if (!c_) return false; auto re = av_opt_set(c_->priv_data, key, val, 0); if (re != 0) { cerr << "av_opt_set failed!"; PrintErr(re); }
return true; }
bool XCodec::SetOpt(const char* key, int val) { unique_lock<mutex> lock(mux_); if (!c_) return false; auto re = av_opt_set_int(c_->priv_data, key, val, 0); if (re != 0) { cerr << "av_opt_set failed!"; PrintErr(re); }
return true; }
bool XCodec::Open() { unique_lock<mutex> lock(mux_); if (!c_) return false; auto re = avcodec_open2(c_, NULL, NULL); if (re != 0) { PrintErr(re); return false; }
return true; }
AVFrame* XCodec::CreateFrame() { unique_lock<mutex> lock(mux_); if (!c_) return false;
auto frame = av_frame_alloc(); frame->width = c_->width; frame->height = c_->height; frame->format = c_->pix_fmt; auto re = av_frame_get_buffer(frame, 0); if (re != 0) { av_frame_free(&frame); PrintErr(re); return nullptr; } return frame; }
|
Create()
作用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| AVCodecContext* XCodec::Create(int codec_id, bool is_encode) { const AVCodec *codec = nullptr; if(is_encode) codec = avcodec_find_encoder((AVCodecID)codec_id); else codec = avcodec_find_decoder((AVCodecID)codec_id); if (!codec) { cerr << "avcodec_find_encoder failed! " << codec_id << endl; return nullptr; }
auto c = avcodec_alloc_context3(codec); if (!c) { cerr << "avcodec_alloc_context3 failed! " << codec_id << endl; return nullptr; }
c->time_base = { 1, 25 }; c->pix_fmt = AV_PIX_FMT_YUV420P; c->thread_count = 16;
return c; }
|
编码器\解码器上下文:
在 FFmpeg 中,AVCodecContext
是一个非常重要的结构体,用于存储编解码器的上下文信息。这个上下文包含了大量关于编解码器的配置、状态和其他重要的信息,用于控制和管理音视频的编码和解码过程。
作用和功能:
- 参数配置:
AVCodecContext
包含了编码或解码过程中需要的大量参数,例如编码器的比特率、帧率、分辨率、采样率、像素格式等。这些参数可以通过 AVCodecContext
进行设置和调整,以控制编解码器的行为。
- 状态管理:
- 编解码器的状态信息(如当前处理的帧、缓冲区状态等)都保存在
AVCodecContext
中。在编码或解码过程中,上下文会随着处理的进展而更新,确保每一步都能正确进行。
- 资源管理:
AVCodecContext
还负责管理编解码过程中使用的各种资源,包括内部的缓冲区、滤波器、线程等。这些资源在上下文被分配时初始化,并在上下文被释放时清理。
- 接口与扩展:
- 通过
AVCodecContext
,用户可以调用 FFmpeg 提供的各种接口函数,如 avcodec_open2
、avcodec_send_packet
、avcodec_receive_frame
等。这些函数以上下文为参数,执行具体的编解码操作。
set_c()
作用:用于将创建的上下文c赋值给类中的上下文c_,若传入的参数为nullptr,则相当于清理上下文资源。
设置对象的编码器上下文 上下文传递到对象中,资源由XEncode维护
线程安全 需要+锁
@para c 编码器上下文 如果c_不为nullptr,则先清理资源,然后再赋值
1 2 3 4 5 6 7 8 9 10
| void XCodec::set_c(AVCodecContext* c) { unique_lock<mutex> lock(mux_); if (c_) { avcodec_free_context(&c_); }
this->c_ = c; }
|
SetOpt()——接受字符串参数
作用:用于设置编解码器上下文的选项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| bool XCodec::SetOpt(const char* key, const char* val) { unique_lock<mutex> lock(mux_); if (!c_) return false;
auto re = av_opt_set(c_->priv_data, key, val, 0); if (re != 0) { cerr << "av_opt_set failed!"; PrintErr(re); }
return true; }
|
av_opt_set(c_->priv_data, key, val, 0)
其中的key
,val
参数
视频编码器常用选项
preset
:
- 描述:预设选项,用于快速配置编码器的参数,影响编码速度和质量。
- 可能值:
ultrafast
, superfast
, veryfast
, faster
, fast
, medium
, slow
, slower
, veryslow
- 示例:
key = "preset"
, val = "fast"
crf
(适用于如 libx264
编码器):
- 描述:恒定质量参数,控制视频的质量与文件大小之间的平衡。
- 可能值:范围从 0 到 51,值越小质量越高,文件越大。
- 示例:
key = "crf"
, val = "23"
bitrate
:
- 描述:目标比特率,控制视频的编码比特率。
- 可能值:任何正整数,通常以
k
为单位表示。
- 示例:
key = "bitrate"
, val = "2000k"
profile
:
- 描述:编码器的配置档案。
- 可能值:
baseline
, main
, high
- 示例:
key = "profile"
, val = "high"
g
(GOP 大小):
- 描述:关键帧间隔,控制两个关键帧之间的帧数。
- 可能值:任何正整数。
- 示例:
key = "g"
, val = "250"
音频编码器常用选项
bit_rate
:
- 描述:目标音频比特率。
- 可能值:任何正整数,通常以
k
为单位表示。
- 示例:
key = "bit_rate"
, val = "128k"
sample_rate
:
- 描述:音频采样率。
- 可能值:常见值有
44100
, 48000
, 96000
等。
- 示例:
key = "sample_rate"
, val = "44100"
channels
:
- 描述:音频通道数。
- 可能值:
1
(单声道), 2
(立体声)
- 示例:
key = "channels"
, val = "2"
其他常用选项
threads
:
- 描述:线程数量,控制编码或解码时使用的线程数量。
- 可能值:任何正整数。
- 示例:
key = "threads"
, val = "4"
max_b_frames
:
- 描述:最大 B 帧数量。
- 可能值:任何正整数。
- 示例:
key = "max_b_frames"
, val = "2"
SetOpt()——接收整数参数
1 2 3 4 5 6 7 8 9 10 11 12 13
| bool XCodec::SetOpt(const char* key, int val) { unique_lock<mutex> lock(mux_); if (!c_) return false; auto re = av_opt_set_int(c_->priv_data, key, val, 0); if (re != 0) { cerr << "av_opt_set failed!"; PrintErr(re); }
return true; }
|
上述SetOpt()的区别
- 参数类型:
- 之前的
SetOpt
函数接受字符串类型的选项值,适用于设置那些需要字符串的选项(如编码器预设、编码配置档案等)。
- 新的
SetOpt
函数接受整数类型的选项值,适用于设置那些需要整数的选项(如比特率、线程数量、GOP 大小等)。
- 使用的 FFmpeg 函数:
- 之前的
SetOpt
函数使用 av_opt_set
来设置字符串类型的选项。
- 新的
SetOpt
函数使用 av_opt_set_int
来设置整数类型的选项。
示例
以下是使用两个 SetOpt
函数的示例:
1 2 3 4 5 6 7 8 9 10
| XCodec codec;
codec.SetOpt("preset", "fast"); codec.SetOpt("profile", "high");
codec.SetOpt("bitrate", 2000000); codec.SetOpt("threads", 4); codec.SetOpt("g", 250);
|
在这些示例中,SetOpt
函数被用来设置各种编解码器选项,以配置编码和解码行为。通过这些选项,可以灵活地控制编码和解码的参数,满足具体的音视频处理需求。
Open()
作用:打开编解码器,线程安全
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| bool XCodec::Open() { unique_lock<mutex> lock(mux_); if (!c_) return false;
auto re = avcodec_open2(c_, NULL, NULL); if (re != 0) { PrintErr(re); return false; }
return true; }
|
avcodec_open2
是 FFmpeg 库中的一个函数,用于初始化一个编解码器上下文,以便进行编码或解码操作。该函数会根据指定的编解码器打开 AVCodecContext
,并分配必要的资源。
函数原型
1
| int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
|
参数解释
AVCodecContext *avctx
:
- 指向要初始化的编解码器上下文的指针。这个上下文必须已经通过
avcodec_alloc_context3
分配,并设置了基本参数。
const AVCodec *codec
:
- 指向要使用的编解码器的指针。如果传递
NULL
,则使用上下文中已经设置的编解码器。
AVDictionary **options
:
- 一个指向字典的指针,用于传递额外的选项和参数。如果不需要传递额外的选项,可以传递
NULL
。
示例解释
1
| int result = avcodec_open2(c_, NULL, NULL);
|
在这个示例中:
c_
:指向要初始化的 AVCodecContext
的指针。
NULL
(第一个):表示使用上下文中已经设置的编解码器。
NULL
(第二个):表示没有额外的选项需要传递。
CreateFrame()
作用:根据AVcodecContext 创建一个AVFrame 需要调用者释放av_frame_free
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| AVFrame* XCodec::CreateFrame() { unique_lock<mutex> lock(mux_); if (!c_) return false;
auto frame = av_frame_alloc(); frame->width = c_->width; frame->height = c_->height; frame->format = c_->pix_fmt; auto re = av_frame_get_buffer(frame, 0); if (re != 0) { av_frame_free(&frame); PrintErr(re); return nullptr; } return frame; }
|
通过 AVCodecContext
创建 AVFrame
可以确保帧的参数与编解码器的参数一致,简化编码和解码流程,保证资源分配正确,并提高数据处理效率。这样可以有效减少编码和解码过程中可能出现的错误,提高程序的稳定性和性能。