FFMPEG完美入门资料---003---FFmpeg 架构

news2025/1/14 1:23:58

3.1 FFmpeg 文件结构

  • libavformat 主要存放 ffmpeg 支持的 各种编解码器 的实现及 ffmpeg 编解码 功能相关的数

文件简要说明
allcodecs.c简单的注册类函数
avcodec.h编解码相关结构体定义和函数原型声明
dsputil.c限幅数组初始化
dsputil.h限幅数组声明
imgconvert.c颜色空间转换相关函数实现
imgconvert_template.h颜色空间转换相关结构体定义和函数声明
utils_codec.c一些解码相关的工具类函数的实现
mpeg4audio.cmpeg4 音频编解码器的函数实现
mpeg4audio.hmpeg4 音频编解码器的函数声明
mpeg4data.hmpeg4 音视频编解码器的公用的函数声明及数据结构定义
mpeg4video.cmpeg4 视频编解码器的函数实现
mpeg4video.hmpeg4 视频编解码器的函数的声明及先关数据结构的定义
mpeg4videodec.cmpeg4 视频解码器的函数实现
mpeg4videoenc.cmpeg4 视频编码器的函数实现
  • libavformat 本目录主要存 放 FFMPEG 支持 的各种媒体格 式 MUXER/DEMUXER 和数据流协议 的定义和实现 文件以及 ffmpeg 解复用 相关的数据结 构及函数定义

文件简要说明
allformats.c简单注册类函数
avformat.h文件和媒体格式相关函数声明和数据结 构定义
avio.c无缓冲 IO 相关函数实现
avio.h无缓冲 IO 相关结构定义和函数声明
aviobuf.c有缓冲数据 IO 相关函数实现
cutils.c简单的字符串操作函数
utils_format.c文件和媒体格式相关的工具函数的实现
file.c文件 io 相关函数
……其他相关媒体流 IO 的函数和数据结构实 现文件。 如:rtsp、http 等。
avi.cAVI 格式的相关函数定西
avi.hAVI 格式的相关函数声明及数据结构定义
avidec.cAVI 格式 DEMUXER 相关函数定义
avienc.cAVI 格式 MUXER 相关函数定义
……其他媒体格式的 muxer/demuxer 相关函 数及数据结构定义和声明文件

*libavutil 主要存放 ffmpeg 工具类 函数的定义

avutil.h简单的像素格式宏定义
bswap.h简单的大小端转换函数的实现
commom.h公共的宏定义和简单函数的实现
mathematics.c数学运算函数实现
rational.h分数相关表示的函数实现

3.2 I\O 模块分析

3.2.1 概述

ffmpeg 项目的数据 IO 部分主要是在 libavformat 库中实现, 某些对于内存的操作部分在 libavutil 库中。数据 IO 是基于文件格式(Format)以及文件传输协议(Protocol) 的, 与具体的编解码标准无关。 ffmpeg 工程转码时数据 IO 层次关系如图所示:

 

对于上面的数据 IO 流程, 具体可以用下面的例子来说明, 我们从一个 http 服务器 获取音视频数据, 格式是 flv 的, 需要通过转码后变成 avi 格式, 然后通过 udp 协议进 行发布。 其过程就如下所示:

  • 1、读入 http 协议数据流, 根据 http 协议获取真正的文件数据(去除无关报文信 息);

  • 2、根据 flv 格式对数据进行解封装;

  • 3、读取帧进行转码操作;

  • 4、按照目标格式 avi 进行封装;

  • 5、通过 udp 协议发送出去。

3.2.2 相关数据结构介绍

在 libavformat 库中与数据 IO 相关的数据结构主要有 URLProtocol、URLContext、ByteIOContext、AVFormatContext 等, 各结构之间的关系如图所示。

 

1、URLProtocol 结构

表示广义的输入文件, 该结构体提供了很多的功能函数, 每一种广义的输入文件 (如:file、pipe、tcp、rtp 等等)对应着一个 URLProtocol 结构,在 av_register_all() 中将该结构体初始化为一个链表, 表头为 avio.c 里的 URLProtocol *first_protocol = NULL;保存所有支持的输入文件协议, 该结构体的定义如下:

typedef struct URLProtocol 
{ 
const char *name; 
int (*url_open)(URLContext *h, const char *url, int flags); 
int (*url_read)(URLContext *h, unsigned char *buf, int size);
int (*url_write)(URLContext *h, const unsigned char *buf, int size); 
int64_t (*url_seek)(URLContext *h, int64_t pos, int whence); 
int (*url_close)(URLContext *h); 
struct URLProtocol *next; 
int (*url_read_pause)(URLContext *h, int pause);
int64_t (*url_read_seek)(URLContext *h, int stream_index,int64_t timestamp, int flags);
int (*url_get_file_handle)(URLContext *h);
int priv_data_size;
const AVClass *priv_data_class; 
int flags;
int (*url_check)(URLContext *h, int mask);
} URLProtocol;

注意到, URLProtocol 是一个链表结构, 这是为了协议的统一管理, ffmpeg 项目中 将所有的用到的协议都存放在一个全局变量 first_protocol 中, 协议的注册是在 av_register_all 中完成的, 新添加单个协议可以调用 av_register_protocol2 函数实 现。 而协议的注册就是将具体的协议对象添加至 first_protocol 链表的末尾。

URLProtocol 在各个具体的文件协议中有一个具体的实例,如在 file 协议中定义为:

URLProtocol ff_file_protocol = {
 .name = " file" ,
 .url_open = file_open,
 .url_read = file_read,
 .url_write = file_write,
 .url_seek = file_seek,
 .url_close = file_close,
 .url_get_file_handle = file_get_handle, .
 .url_check = file_check,
};

★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。

见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

 

2、URLContext 结构

URLContext 提供了与当前打开的具体的文件协议(URL)相关数据的描述, 在该结 构中定义了指定当前 URL(即 filename 项)所要用到的具体的 URLProtocol, 即:提供 了一个在 URLprotocol 链表中找到具体项的依据, 此外还有一些其它的标志性的信息, 如 flags, is_streamed 等。 它可以看成某一种协议的载体。 其结构定义如下:

typedef struct URLContext
 {
const AVClass *av_class; ///< information for av_log(). Set by url_open(). 
struct URLProtocol *prot; 
int flags; 
int is_streamed; /**< true if streamed (no seek possible), default = false *
int max_packet_size; void *priv_data; 
char *filename; /**< specified URL */ 
int is_connected;
 } URLContext;

那么 ffmpeg 依据什么信息初始化 URLContext?然后又是如何初始化 URLContext 的呢?

在打开一个 URL 时, 全局函数 ffurl_open 会根据 filename 的前缀信息来确定 URL 所使用的具体协议, 并为该协议分配好资源, 再调用 ffurl_connect 函数打开具体协议, 即调用协议的 url_open, 调用关系如下:

int av_open_input_file(AVFormatContext **ic_ptr,
const char *filename,
AVInputFormat *fmt,
int buf_size,
AVFormatParameters *ap)
​
int avformat_open_input(
AVFormatContext **ps ,
const char *filename ,
AVInputFormat *fmt,
AVDictionary **options)
​
static int init_input(AVFormatContext *s, const char *filename)

 

浅蓝色部分的函数完成了 URLContext 函数的初始化,URLContext 使 ffmpeg 外所暴 露的接口是统一的,而不是对于不同的协议用不同的函数,这也是面向对象思维的体现。 在此结构中还有一个值得说的是 priv_data 项, 这是结构的一个可扩展项, 具体协议可 以根据需要添加相应的结构, 将指针保存在这就行。

3、AVIOContext 结构

AVIOContext(即:ByteIOContext)是由 URLProtocol 和 URLContext 结构扩展而 来,也是 ffmpeg 提供给用户的接口,它将以上两种不带缓冲的读取文件抽象为带缓冲的 读取和写入, 为用户提供带缓冲的读取和写入操作。 数据结构定义如下:

typedef struct 
{
  unsigned char *buffer; /**< Start of the buffer. */
  int buffer_size; /**< Maximum buffer size */ 
  unsigned char *buf_ptr; /**< Current position in the buffer */
  unsigned char *buf_end; 
  void *opaque; /关联 URLContext int (*read_packet)(void *opaque, uint8_t *buf, int buf_size); 
  int (*write_packet)(void *opaque, uint8_t *buf, int buf_size);
  int64_t (*seek)(void *opaque, int64_t offset, int whence); 
  int64_t pos; int must_flush; int eof_reached; /**< true if eof reached */
  int write_flag; /**< true if open for writing */
  int max_packet_size; 
  unsigned long checksum; 
  unsigned char *checksum_ptr; 
  unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);
  int error; 
  int (*read_pause)(void *opaque, int pause) int64_t (*read_seek)(void *opaque, int stream_index,int64_t timestamp, int flags); 
  int seekable;
 } AVIOContext;

结 构 简 单 的 为 用 户 提 供 读 写 容 易 实 现 的 四 个 操 作 , read_packet write_packet read_pause read_seek, 极大的方便了文件的读取, 四个函数在加了缓冲机制后被中转 到, URLContext 指向的实际的文件协议读写函数中。

下面给出 0.8 版本中是如何将 AVIOContext 的读写操作中转到实际文件中的。

在 avio_open()函数中调用了 ffio_fdopen()函数完成了对 AVIOContex 的初始 化, 其调用过程如下:

 

蓝色部分的函数调用完成了对 AVIOContext 的初始化, 在初始化的过程中, 将 AVIOContext 的 read_packet 、 write_packet 、 seek 分 别 初 始 化 为 : ffurl_read ffurl_write ffurl_seek , 而 这 三 个 函 数 又 将 具 体 的 读 写 操 作 中 转 为 : h->prot->url_read、h->prot->url_write、h->prot->url_seek, 另外两个变量初始化 时也被相应的中转, 如下:

(*s)->read_pause = (int (*)(void *, int))h->prot->url_read_pause;
(*s)->read_seek = (int64_t (*)(void *, int, int64_t, int))h->prot->url_read_seek;

所以, 可以简要的描述为:AVIOContext 的接口口是加了缓冲后的 URLProtocol 的 函数接口。

在 aviobuf.c 中定义了一系列关于 ByteIOContext 这个结构体的函数, 如下

  • put_xxx 系列:

void put_byte(ByteIOContext *s, int b); 
void put_buffer(ByteIOContext *s, const unsigned char *buf, int size);
void put_le64(ByteIOContext *s, uint64_t val); 
void put_be64(ByteIOContext *s, uint64_t val); 
void put_le32(ByteIOContext *s, unsigned int val); 
void put_be32(ByteIOContext *s, unsigned int val); 
void put_le24(ByteIOContext *s, unsigned int val); 
void put_be24(ByteIOContext *s, unsigned int val);
void put_le16(ByteIOContext *s, unsigned int val); 
void put_be16(ByteIOContext *s, unsigned int val); 
void put_tag(ByteIOContext *s, const char *tag);

*get_xxx 系列:

int get_buffer(ByteIOContext *s, unsigned char *buf, int size);
int get_partial_buffer(ByteIOContext *s, unsigned char *buf, int size); 
int get_byte(ByteIOContext *s);
unsigned int get_le24(ByteIOContext *s);
unsigned int get_le32(ByteIOContext *s); 
uint64_t get_le64(ByteIOContext *s);
unsigned int get_le16(ByteIOContext *s);
char *get_strz(ByteIOContext *s, char *buf, int maxlen); 
unsigned int get_be16(ByteIOContext *s); 
unsigned int get_be24(ByteIOContext *s); 
unsigned int get_be32(ByteIOContext *s);
uint64_t get_be64(ByteIOContext *s);

这些 put_xxx 及 get_xxx 函数是用于从缓冲区 buffer 中写入或者读取若干个字节, 对于读写整型数据,分别实现了大端和小端字节序的版本。而缓冲区 buffer 中的数据又 是 从 何 而 来 呢 , 有 一 个 fill_buffer 的 函 数 , 在 fill_buffer 函 数 中 调 用 了 ByteIOContext 结构的 read_packet 接口。 在调用 put_xxx 函数时, 并没有直接进行真 正写入操作,而是先缓存起来,直到缓存达到最大限制或调用 flush_buffer 函数对缓冲 区进行刷新, 才使用 write_packet 函数进行写入操作。

3.3 Demuxer 和 muxer 模块分析

3.3.1 概述

ffmpeg 的 demuxer 和 muxer 接口分别在 AVInputFormat 和 AVOutputFormat 两个结

构体中实现, 在 av_register_all()函数中将两个结构分别静态初始化为两个链表, 保 存在全局变量:first_iformat 和 first_oformat 两个变量中。在 FFmpeg 的文件转换或 者打开过程中, 首先要做的就是根据传入文件和传出文件的后缀名匹配合适的 demuxer 和 muxer, 得到合适的信息后保存在 AVFormatContext 中。

3.3.2 相关数据结构介绍

1、AVInputFormat

该结构被称为 demuxer, 是音视频文件的一个解封装器, 它的定义如下:

typedef struct AVInputFormat
 {
 const char *name; 
 const char *long_name;
 int priv_data_size; //具体文件容器格式对应的 Context 的大小, 如:avicontext int (*read_probe)(AVProbeData *); 
 int (*read_header)(struct AVFormatContext *, AVFormatParameters *ap); 
 int (*read_packet)(struct AVFormatContext *, AVPacket *pkt); 
 int (*read_close)(struct AVFormatContext *); 
 #if FF_API_READ_SEEK 
 attribute_deprecated int (*read_seek)(struct AVFormatContext *, int stream_index, int64_t timestamp, int flags);
  #endif 
 int64_t (*read_timestamp)(struct AVFormatContext *s, int stream_index, int64_t *pos, int64_t pos_limit);
  int flags; const char *extensions;
  int value; 
  int (*read_play)(struct AVFormatContext *);
  int (*read_pause)(struct AVFormatContext *); 
  const struct AVCodecTag * const *codec_tag;
  int (*read_seek2)(struct AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags);
  #if FF_API_OLD_METADATA2
 const AVMetadataConv *metadata_conv;
  #endif 
  const AVClass *priv_class; ///< AVClass for the private context
  struct AVInputFormat *next; 
 } AVInputFormat;

对于不同的文件格式要实现相应的函数接口, 这样每一种格式都有一个对应的 demuxer, 所有的 demuxer 都保存在全局变量 first_iformat 中。 红色表示提供的接口。

2、AVOutputFormat

该结构与 AVInputFormat 类似也是在编译时静态初始化, 组织为一个链表结构, 提 供了多个 muxer 的函数接口。

 int (*write_header)(struct AVFormatContext *);
 int (*write_packet)(struct AVFormatContext *, AVPacket *pkt); 
 int (*write_trailer)(struct AVFormatContext *);

对于不同的文件格式要实现相应的函数接口, 这样每一种格式都有一个对应的 muxer, 所有的 muxer 都保存在全局变量 first_oformat 中。

3、AVFormatContext

该结构表示与程序当前运行的文件容器格式使用的上下文, 着重于所有文件容器共 有的属性,在运行时动态的确定其值,是 AVInputFormat 和 AVOutputFormat 的载体,但 同一个结构对象只能使 AVInputFormat 和 AVOutputFormat 中的某一个有效。每一个输入 和输出文件, 都在

static AVFormatContext *output_files[MAX_FILES] 

static AVFormatContext *input_files[MAX_FILES];

定义的指针数组全局变量中有对应的实体。 对于输入和输出, 因为共用的是同一个结构 体, 所以需要分别对该结构中如下定义的 iformat 或 oformat 成员赋值。 在转码时读写 数据是通过 AVFormatContext 结构进行的。 定义如下:

typedef struct AVFormatContext 
{ 
 const AVClass *av_class; 
 struct AVInputFormat *iformat; //指向具体的 demuxer
 struct AVOutputFormat *oformat; //指向具体的 muxer
 void *priv_data; //具体文件容器格式的 Context 如:avicontext
 AVIOContext *pb; //广义的输入输出;
 unsigned int nb_streams; //本次打开的文件容器中流的数量
 AVStream **streams; //每个流的相关描述
 char filename[1024]; // input or output filename */ 
 int64_t timestamp; 
 int ctx_flags;
 struct AVPacketList *packet_buffer;
 …… 
 enum CodecID video_codec_id; 
 enum CodecID audio_codec_id;
 enum CodecID subtitle_codec_id; 
 unsigned int max_index_size;
 unsigned int max_picture_buffer; 
 …… 
 struct AVPacketList *raw_packet_buffer; 
 struct AVPacketList *raw_packet_buffer_end; 
 struct AVPacketList *packet_buffer_end;
  …… 
 } AVFormatContext;

红色部分的成员是 AVFormatContext 中最为重要的成员变量, 这些变量的初始化是 ffmpeg 能正常工作的必要条件, 那么, AVFormatContext 是如何被初始化的呢?文件的 格式是如何被探测到的呢?

首先我们来探讨:

 struct AVInputFormat *iformat; //指向具体的 demuxer
 struct AVOutputFormat *oformat; //指向具体的 muxer 
 void *priv_data; //具体文件容器格式的 Context 如:avicontext

三个成员的初始化。

在 avformat_open_input() 函 数 中 调 用 了 init_input() 函 数 , 然 后 用 调 用 了 av_probe_input_format()函数实现了对 AVFormatContext 的初始化。其调用关系如下:

 int av_open_input_file(AVFormatContext **ic_ptr, const char *filename, AVInputFormat *fmt, int buf_size, AVFormatParameters *ap);
 int avformat_open_input(ic_ptr, filename, fmt, &opts);
 static int init_input(s, filename);
 av_probe_input_format(&pd, 0);

av_probe_input_format (AVProbeData *pd, int is_opened, int *score_max) 函数用途是根据传入的 probe data 数据, 依次调用每个 demuxer 的 read_probe 接口, 来进行该 demuxer 是否和传入的文件内容匹配的判断。 与 demuxer 的匹配不同, muxer 的匹配是调用 guess_format 函数, 根据 main( ) 函数的 argv 里的输出文件后缀名来进 行的。 至此完成了前三个重要成员的初始化, 具体的做法就不在深入分析。

下面分别给出 av_read_frame 函数以及 av_write_frame 函数的基本流程。

int av_read_frame(AVFormatContext *s, AVPacket *pkt);
 ->av_read_frame_internel
    ->av_read_packet 
       ->iformat->read_packet(在实现中会丢弃多余信息) 
          ->av_get_packet 
             ->get_xxx
​
int av_write_frame(AVFormatContext *s, AVPacket *pkt);
  ->oformat->write_packet
     ->put_xxx

由上可见, 对 AVFormatContext 的读写操作最终是通过 ByteIOContext 来实现的, 这样, AVFormatContext 与 URLContext 就由 ByteIOContext 结构联系到一起了。 在 AVFormat 结构体中有一个 packet 的缓冲区 raw_packet_buffer, 是 AVPackList 的指针 类型, av_read_packet 函数将读到的包添加至 raw_packet_buffer 链表末尾。

3.4 Decoder/Encoder 模块

3.4.1 概述

编解码模块主要包含的数据结构为:AVCodec、AVCodecContext 每一个解码类型都 会有自己的 Codec 静态对像, Codec 的 int priv_data_size 记录该解码器上下文的结构 大 小 , 如 MsrleContext 。 这 些 都 是 编 译 时 确 定 的 , 程 序 运 行 时 通 过 avcodec_register_all()将所有的解码器注册成一个链表。在 av_open_input_stream() 函数中调用 AVInputFormat 的 read_header()中读文件头信息时, 会读出数据流的 CodecID, 即确定了他的解码器 Codec。

在 main()函数中除了解析传入参数并初始化 demuxer 与 muxer 的 parse_options( ) 函数以外, 其他的功能都是在 av_encode( )函数里完成的。 在 libavcodec\utils.c 中 有 如 下 二 个 函 数 :AVCodec *avcodec_find_encoder(enum CodecID id) 和 AVCodec *avcodec_find_decoder(enum CodecID id)他们的功能就是根据传入的 CodecID, 找到 匹配的 encoder 和 decoder。在 av_encode( )函数的开头,首先初始化各个 AVInputStream 和 AVOutputStream,然后分别调用上述二个函数,并将匹配上的 encoder 与 decoder 分 别保存在:

AVInputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codec 

AVOutputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codec 

变量。

3.4.2 相关数据结构的初始化

AVCodecContext 结构

AVCodecContext 保存 AVCodec 指针和与 codec 相关数据,如 video 的 width、height, audio 的 sample rate 等。

AVCodecContext 中的 codec_type, codec_id 二个变量对于 encoder/decoder 的匹 配来说, 最为重要。

enum CodecType codec_type; /* see CODEC_TYPE_xxx */
enum CodecID codec_id; /* see CODEC_ID_xxx */

如上所示, codec_type 保存的是 CODEC_TYPE_VIDEO, CODEC_TYPE_AUDIO 等媒体类 型, codec_id 保存的是 CODEC_ID_FLV1, CODEC_ID_VP6F 等编码方式。

以支持 flv 格式为例, 在前述的 av_open_input_file(…… ) 函数中, 匹配到正确 的 AVInputFormat demuxer 后,通过 av_open_input_stream( )函数中调用 AVInputFormat 的 read_header 接口来执行 flvdec.c 中的 flv_read_header( )函数。flv_read_header( ) 函数内, 根据文件头中的数据, 创建相应的视频或音频 AVStream, 并设置 AVStream 中 AVCodecContext 的正确的 codec_type 值。codec_id 值是在解码过程。flv_read_packet( ) 函数执行时根据每一个 packet 头中的数据来设置的。

以 avidec 为例 有如下初始化,我们主要知道的就是 code_id 和 code_type 该字段关 联具体的解码器, 和解码类型(音视频或 subtitle)

if (st->codec->stream_codec_tag == AV_RL32(" Axan" )) 
{
  st->codec->codec_id = CODEC_ID_XAN_DPCM;
  st->codec->codec_tag = 0; 
}
 if (amv_file_format) 
{
  st->codec->codec_id = CODEC_ID_ADPCM_IMA_AMV; 
  ast->dshow_block_align = 0; 
}
 break;
 case AVMEDIA_TYPE_SUBTITLE:
    st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; 
    st->request_probe= 1;
    break;
 default: 
   st->codec->codec_type = AVMEDIA_TYPE_DATA; 
   st->codec->codec_id= CODEC_ID_NONE; 
  st->codec->codec_tag= 0; 
avio_skip(pb, size);

3.5 其他重要数据结构的初始化

3.5.1 AVStream

AVStream 结构保存与数据流相关的编解码器,数据段等信息。比较重要的有如下二个成员:

AVCodecContext *codec; /**< codec context */ 
void *priv_data;

其中 codec 指针保存的就是上节所述的 encoder 或 decoder 结构。 priv_data 指针 保存的是和具体编解码流相关的数据,如下代码所示,在 ASF 的解码过程中,priv_data 保存的就是 ASFStream 结构的数据。

AVStream *st;
ASFStream *asf_st; 
… … 
st->priv_data = asf_st;

3.5.2 AVInputStream/ AVOutputStream

根据输入和输出流的不同, 前述的 AVStream 结构都是封装在 AVInputStream 和 AVOutputStream 结构中, 在 av_encode( )函数中使用。 AVInputStream 中还保存的有与 时间有关的信息。 AVOutputStream 中还保存有与音视频同步等相关的信息。

3.5.3 AVPacket

AVPacket 结构定义如下, 其是用于保存读取的 packet 数据。

typedef struct AVPacket
 {
  int64_t pts; ///< presentation time stamp in time_base units
  int64_t dts; ///< decompression time stamp in time_base units
  uint8_t *data;
  int size; 
  int stream_index;
  int flags;
  int duration; ///< presentation duration in time_base units
  void (*destruct)(struct AVPacket *); 
  void *priv; 
  int64_t pos; ///< byte position in stream, -1 if unknown 
  } AVPacket;

在 av_encode()函数中, 调用 AVInputFormat 的

(*read_packet)(struct AVFormatContext *, AVPacket *pkt)

接口, 读取输入文 件的一帧数据保存在当前输入 AVFormatContext 的 AVPacket 成员中。

作者:张芳涛

★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。

见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/145533.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

剑指offer(C++)-JZ63:买卖股票的最好时机(一)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 题目描述&#xff1a; 假设你有一个数组prices&#xff0c;长度为n&#xff0c;其中prices[i]是股票在第i天的价格&#xff0c;请…

FreeMen正式上线,让工作更自由

“让工作更自由”&#xff0c;开屏页上六个大字宣告着FreeMen正式上线&#xff0c;全新的FreeMen APP也正式登录各大手机应用市场。作为一款专注IT技术者圈子的APP&#xff0c;其上线标志着助力程序员职业道路上向前迈进一大步。 FreeMen相关负责人表示&#xff0c;基本上10个职…

基于java+Springboot操作系统教学交流平台详细设计实现

基于javaSpringboot操作系统教学交流平台详细设计实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源…

【C语言】使用指针时出现的各种错误总结以及处理方法

&#x1f3d6;️作者&#xff1a;malloc不出对象 ⛺专栏&#xff1a;《初识C语言》 &#x1f466;个人简介&#xff1a;一名双非本科院校大二在读的科班编程菜鸟&#xff0c;努力编程只为赶上各位大佬的步伐&#x1f648;&#x1f648; 目录前言一、什么是野指针二、野指针出现…

Redis主从哨兵集群

主从 主从同步原理 1.全量同步 1.从节点刚连接到主节点时&#xff0c;主动请求数据同步 2.主节点判断是否是第一次同步&#xff0c;如果是&#xff0c;就返回主节点的数据版本信息 3.随后&#xff0c;主节点执行bgsave&#xff0c;生成RDB文件&#xff0c;并发送给从节点 4.这…

Java时间类

时间在Java中是两个类 Date 和 Calendar Date类 Date是在Java.util包中的也就是工具包。 测试 输出的是当前的系统时间 修改idea中的注释的颜色&#xff1a; 是date不是data package org.data; import java.util.Date; public class Test { public static void main(…

【Oracle】20230106PLSQL中文不显示,全部为?解决流程

问题&#xff1a;查询的表中有中文数据&#xff0c;全部显示为&#xff1f; 解决途径1&#xff1a;修改注册表以及环境变量&#xff08;改系统变量&#xff01;不要只改用户变量&#xff09; Oracle使用——PLSQL的中文乱码显示全是问号_海蓝树的博客-CSDN博客_plsql无法显示…

上线网站详细介绍(服务器购买-域名申请-SSL证书申请)

文章目录上线之前的准备工作&#xff0c;通俗的来讲&#xff1a;服务器是什么-云服务器购买域名解释-域名备案-域名如何申请①什么是域名&#xff0c;为什么要域名&#xff1f;②为什么非要域名备案和不备案的区别&#xff1f;③自己的主机怎么备案-自己的电脑可以作为服务器吗…

系分架构 - 软件架构设计

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录系分&架构 - 软件架构设计考点摘要概念架构的 4 1 视图软件架构风格经典五大架构风格调用/返回风格独立构件风格虚拟机风格仓库风格层次架构风格二层C/S架构三层C/S架构B/S架构混合架构风格闭环控制架…

Linux 软件管理 RPM 管理工具

概念引入 &#xff1a; # 首先提出一个问题&#xff0c;我们在 Linux 操作系统中是如何 安装软件的 &#xff1f;&#xff1f; >>> 在 Linux 系统中&#xff0c;安装软件是有三种方式 >>> 第一种 &#xff1a; RPM 管理工具 第二种 &#xff1a; …

如何在新环境接手项目?(上)【洞见2】

01、世界真实情况根据IDC机构在2022年统计显示&#xff0c;截止2021年我国中小企业数量已达到4881万家&#xff0c;同比增长8.5%。中小企业的行业分布不均匀&#xff0c;超过70%的中小企业分布于专业服务业&#xff08;含互联网和科技服务&#xff09;、物流批发、零售和制造业…

剑指offer----C语言版----第十一天

目录 1. 数值的整数次方 1.1 运行超时的思路 1.2 思路一: 快速幂 (递归实现) 1.3 思路二: 快速幂 (迭代实现) 1. 数值的整数次方 原题链接: 剑指 Offer 16. 数值的整数次方 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/shu-zhi-de-zheng-shu-ci-f…

web3学习:什么是以太坊

以太网是“世界的计算机”&#xff0c;这是以太坊平台的一种常见描述。这是什么意思呢&#xff1f;让我们首先从关注计算机科学的描述开始&#xff0c;然后对以太坊的功能和特性进行更实际的解读&#xff0c;并将其与比特币和其他分布式账本技术&#xff08;简单起见&#xff0…

深度学习笔记:神经网络(2)

对于神经网络上一篇文章&#xff0c;可见&#xff1a;https://blog.csdn.net/Raine_Yang/article/details/128473486?spm1001.2014.3001.5501 神经网络各层信号传递的实现&#xff1a; 对于全连接网络&#xff0c;连接权重的个数为&#xff08;前一层神经元总数 * 后一次神经…

el-table(type=“selection“)多选框两种回显

目录 一、前端数据回显&#xff08;页面间数据展示&#xff09; 1、图篇帮助理解 2、描述&#xff1a; 3、代码 4、两个API&#xff0c;一个v-model 二、数据库数据回显 1、描述&#xff1a; 2、核心代码: 3、比较完整代码&#xff1a;(这是element ui官方文档上的) 4…

XMLHttpRequest 对象(AJAX通信)

1.XMLHttpRequest 对象是什么&#xff1f; 浏览器与服务器之间&#xff0c;采用 HTTP 协议通信。用户在浏览器地址栏键入一个网址&#xff0c;或者通过网页表单向服务器提交内容&#xff0c;这时浏览器就会向服务器发出 HTTP 请求。 1999年&#xff0c;微软公司发布 IE 浏览器…

基于STM32F411使用SPI+DMA驱动LCD

先看效果 F411CE 硬件SPI&#xff0b;DMA驱动LCD基于HAL库 其实HAL库我用不太习惯&#xff0c;一直也是用的标准库 但HAL库确实是好上手一些&#xff0c;就迅速创建一个新的template 这次就当尝试一下吧&#xff0c;因为用的比较少&#xff0c;我会记录的详细 如图点击&…

火山引擎DataLeap数据调度实例的 DAG 优化方案

更多技术交流、求职机会&#xff0c;欢迎关注字节跳动数据平台微信公众号&#xff0c;并进入官方交流群 实例 DAG 介绍 DataLeap 是火山引擎自研的一站式大数据中台解决方案&#xff0c;集数据集成、开发、运维、治理、资产管理能力于一身的大数据研发治理套件。在平台中&…

从0到1完成一个Vue后台管理项目(四、引入axios、配置路由)

往期 从0到1完成一个Vue后台管理项目&#xff08;一、创建项目&#xff09; 从0到1完成一个Vue后台管理项目&#xff08;二、使用element-ui&#xff09; 从0到1完成一个Vue后台管理项目&#xff08;三、使用SCSS/LESS&#xff0c;安装图标库&#xff09; axios axios就是…

手握智算中心“绿洲”,毫末跑在中美自动驾驶长跑第一线

作者 | 白日梦想家 编辑 | 王博2022年过去&#xff0c;数据驱动成为自动驾驶演进共识。沿着数据驱动这条路线&#xff0c;自动驾驶加速迈入智算时代。 智算中心应运而生。 实际上&#xff0c;将智算引入自动驾驶的开先河者是特斯拉&#xff0c;其率先发布了专用于自动驾驶训练的…