-------------------------------------------------------------------------
author: hjjdebug
date: 2023年 08月 01日 星期二 11:26:40 CST
descriptor: 从c++的角度来看ffmpeg 的架构
-------------------------------------------------------------------------
1. AVClass 类就是一个普通的类. 基本的类.
AVClass 包含了一个AVOption *option, 这个指针会指向一个AVOption 数组.
-------------------------------------------------------------------------
-------------------------------------------------------------------------
2. ffmpeg 中包含了很多context类, 它们共同继承于baseContext类.
-------------------------------------------------------------------------
这个BaseContext类就是:
class BaseContext{
AVClass *class;
}
比方说AVCodecContext类, 它实际上是 class AVCodecContext::public BaseContext
只是ffmpeg 用c 写成,它并没有显示声明baseContext类, 而是在隐式的使用了.
即它在AVCodecContext 中的第一项 声明为 AVClass *class,
例如: AVCodecContext 类是这样声明的.
typedef struct AVCodecContext {
const AVClass *av_class;
int log_level_offset;
....
};
所谓子类,就是继承了父类的属性和方法.
一个父类领导了一群子类, 这有什么好处呢? 好处有2,
1.所有的子类都像父类,子类指针可以退化为父类指针(子类退化机制)
2.在还没有确认子类到底是什么的时候,父类代码就可以书写了. (父类不依赖于子类)
AVClass 是在libavutil/log.h 中定义的,
由它可以创作出很多实例.查ffmpeg 源代码,有431个对象,其中有的类.class_name=""
举例: 定义了一个udp_class 对象
static const AVClass udp_class = {
.class_name = "udp",
.item_name = av_default_item_name,
.option = options, // 该options 描述的是UDPContext 中的成员.
.version = LIBAVUTIL_VERSION_INT,
};
依据udp_class的options 选项中知道它描述的是UDPContext类, 该类在udp.c 中定义
typedef struct UDPContext {
const AVClass *class; // 指向 udp_class
int udp_fd;
int ttl;
int udplite_coverage;
int buffer_size;
int pkt_size;
int is_multicast;
int is_broadcast;
int local_port;
int reuse_socket;
int overrun_nonfatal;
struct sockaddr_storage dest_addr;
int dest_addr_len;
int is_connected;
/* Circular Buffer variables for use in UDP receive code */
int circular_buffer_size;
AVFifoBuffer *fifo;
int circular_buffer_error;
int64_t bitrate; /* number of bits to send per second */
int64_t burst_bits;
int close_req;
#if HAVE_PTHREAD_CANCEL
pthread_t circular_buffer_thread;
pthread_mutex_t mutex;
pthread_cond_t cond;
int thread_started;
#endif
uint8_t tmp[UDP_MAX_PKT_SIZE+4];
int remaining_in_dg;
char *localaddr;
int timeout;
struct sockaddr_storage local_addr_storage;
char *sources;
char *block;
IPSourceFilters filters;
} UDPContext;
udp_class 对象被 ff_udp_protocol 对象所引用, 是其私有数据类, .priv_data_class
const URLProtocol ff_udp_protocol = {
.name = "udp",
.url_open = udp_open,
.url_read = udp_read,
.url_write = udp_write,
.url_close = udp_close,
.url_get_file_handle = udp_get_file_handle,
.priv_data_size = sizeof(UDPContext), //URLProtocol 中定义这个私有类的大小,此处是UDPContext类
.priv_data_class = &udp_class, //URLProtocol 协议中确实有一项叫priv_data_class,此处是udp_class
.flags = URL_PROTOCOL_FLAG_NETWORK,
};
ff_udp_protocol 对象被url_protocols(URLProtocol 数组) 所引用, 通过枚举URL协议数组可以找到该对象
static const URLProtocol * const url_protocols[] = {
...
&ff_udp_protocol,
}
url_protocols数组在libavformat/protocols.c 中被定义,以函数接口方式提供对外访问.
例如帮助信息,就是通过查找url_protocols->ff_udp_protocol->udp_class->options 来显示其帮助信息
udp_class 被ff_udp_protocol 所包含,其类是URLProtocol,
这些概念都在url.h中定义的(最关键的是URLProtocal, 根据协议可以创建URLContext),顺便再看看其它函数... 在avio.c 中实现
avio.c 中的代码都很简短但有技巧,所谓技巧就是委托,转发.
我目前的理解通常把保留一个对象的指针,当调用一个函数时,去调用这个对象的对应函数叫委托或转发.
而把调用一个函数时,同时传来一个对象指针,我们回调这个对象函数叫策略模式
再举一个AVInputFormat 的例子:
mpegts 是一种输入格式. 它的帮助信息是如何显示的? 如何了解它,掌握它? (从这一点入手,找到与它关联的知识)
我们查 :
$ffmpeg -formats // 显示了所有formats, 其中mpegts 即属于Demuxer 也属于 muxer
$ffmpeg -demuxer // 显示了所有demuxer, 其中MPEG-TS (MPEG-2 Transport Stream)
$ffmpeg -h demuxer=mpegts // 向我们显示了该demuxer 的所有选项
mpegts demuxer AVOptions:
-resync_size <int> .D......... set size limit for looking up a new synchronization (from 0 to INT_MAX) (default 65536)
-fix_teletext_pts <boolean> .D......... try to fix pts values of dvb teletext streams (default true)
-ts_packetsize <int> .D....XR... output option carrying the raw packet size (from 0 to 0) (default 0)
-scan_all_pmts <boolean> .D......... scan and combine all PMTs (default auto)
-skip_unknown_pmt <boolean> .D......... skip PMTs for programs not advertised in the PAT (default false)
-merge_pmt_versions <boolean> .D......... re-use streams when PMT's version/pids change (default false)
从该help 入手, 我们找一下这个demuxer 对象,它是以怎样的数据流来工作的?
static void show_help_demuxer(const char *name)
{
const AVInputFormat *fmt = av_find_input_format(name); // 对象AVInputFormat, 包含私有类指针 fmt->priv_class
if (fmt->priv_class)
show_help_children(fmt->priv_class, AV_OPT_FLAG_DECODING_PARAM,100); //,显示该私有类的帮助信息,此忽略(请参考<ffmpeg 帮助系统>博客)
}
由context类构建对象就不举例了,读代码时仔细体会就可以了.
-------------------------------------------------------------------------
3. ffmpeg 中的对象: 这些对象都是常对象,存在于全局变量中
-------------------------------------------------------------------------
从configure 文件中找到了以下基础列表, 它们还可以组合成其它列表组合. 这里就是ffmpeg的基础对象了.
FILTER_LIST=$(find_filters_extern libavfilter/allfilters.c)
OUTDEV_LIST=$(find_things_extern muxer AVOutputFormat libavdevice/alldevices.c outdev)
INDEV_LIST=$(find_things_extern demuxer AVInputFormat libavdevice/alldevices.c indev)
MUXER_LIST=$(find_things_extern muxer AVOutputFormat libavformat/allformats.c)
DEMUXER_LIST=$(find_things_extern demuxer AVInputFormat libavformat/allformats.c)
ENCODER_LIST=$(find_things_extern encoder AVCodec libavcodec/allcodecs.c)
DECODER_LIST=$(find_things_extern decoder AVCodec libavcodec/allcodecs.c)
PARSER_LIST=$(find_things_extern parser AVCodecParser libavcodec/parsers.c)
BSF_LIST=$(find_things_extern bsf AVBitStreamFilter libavcodec/bitstream_filters.c)
HWACCEL_LIST=$(find_things_extern hwaccel AVHWAccel libavcodec/hwaccels.h)
PROTOCOL_LIST=$(find_things_extern protocol URLProtocol libavformat/protocols.c)
以AVCodec 对象列表为例来说明:
后面每一个地址都是一个AVCodec对象 的地址
static const AVCodec * const codec_list[] = {
&ff_a64multi_encoder,
&ff_a64multi5_encoder,
&ff_alias_pix_encoder,
&ff_amv_encoder,
...
&ff_h264_decoder,
.....
}
以ff_h264_decoder 为例:
ff_h264_decoder 就是一个AVCodec 对象, 由于它是AVCodec 的一个实例,所以对AVCodec 所要求的指针函数都要实现.
属性也都要赋值(或给默认值)
AVCodec ff_h264_decoder = {
.name = "h264",
.long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H264,
.priv_data_size = sizeof(H264Context),
.init = h264_decode_init,
.close = h264_decode_end,
.decode = h264_decode_frame,
.capabilities = /*AV_CODEC_CAP_DRAW_HORIZ_BAND |*/ AV_CODEC_CAP_DR1 |
AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS |
AV_CODEC_CAP_FRAME_THREADS,
.hw_configs = (const AVCodecHWConfigInternal *const []) {
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_EXPORTS_CROPPING |
FF_CODEC_CAP_ALLOCATE_PROGRESS | FF_CODEC_CAP_INIT_CLEANUP,
.flush = h264_decode_flush,
.update_thread_context = ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context),
.profiles = NULL_IF_CONFIG_SMALL(ff_h264_profiles),
.priv_class = &h264_class,
};文章来源:https://www.toymoban.com/news/detail-626121.html
这里AVCodec 在libavcodec/codec.h中声明
它只是一个接口,没有具体的实现,每一个实例都要实现它的接口.
该头文件还声明了几个函数,例如 avcodec_find_decoder,avcodec_find_encoder,avcodec_iterate等
这就在一个简单的文件 allcodecs.c 中来实现了
核心是av_codec_iterate(), 就是查找一个AVCodec 对象表
其它的一堆对象也是如此组织的.文章来源地址https://www.toymoban.com/news/detail-626121.html
到了这里,关于从c++的角度来看ffmpeg 的架构的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!