项目组成
ffmpeg
视频文件转换命令行工具,也支持经过实时电视卡抓取和编码成视频文件.
ffserver
基于http、rtsp用于实时广播的多媒体服务器,支持时间平移
ffplay
用SDL和FFmpeg库开发的一个简单的媒体播放器
libavcodec
一个包含了所有ffmpeg音视频编解码器的库,为了保证最优性能和高可复用性,大多数编解码器从头开发
libavformat
一个包含了所有的普通音视频格式的解析器和产生器的库
ffmpeg命令选项
音频
-ab bitrate 设置音频码率
-ar freq 设置音频采样率
-ac channels 设置通道 缺省1
-an 不能使音频记录
-acodec codec 使用codec编解码
eg:
./ffmpeg –i test.avi -an –r 25 test.mpg <回车>
此时将生成一个没有声音的 test.mpg 视频,再在命令行中输入:
./ffmpeg –i test.mpg –i test.mp3 –r 25 test1.mpg<回车>
此时将会生成一个名为 test1.mpg 的视频。该视频播放时视频为 test.avi 的视频,但音频
变为了 test.mp3 的音频了。
FFmpeg架构
libavcodec
| 文件 |
功能 |
| allcodecs.c |
简单的注册类函数 |
| avcodec.h |
编解码相关结构体定义和函数原型声明 |
| dsputil.c |
限幅数组初始化 |
| utils_codec.c |
一些解码相关的工具类函数的实现 |
| mpeg4audio.c |
mpeg4音频编解码器的函数实现 |
| mpeg4data.h |
mpeg4音视频编解码器的公用的函数声明及数据结构定义 |
| mpeg4video.c |
mpeg4视频编解码器的函数实现 |
| mpeg4videoenc.c |
mpeg4视频编码器的函数实现 |
mpeg4videodec.c|mpeg4视频解码器的函数实现
libavformat
| 文件 |
功能 |
| allformatss.c |
简单的注册类函数 |
| avformat.h |
文件和媒体格式相关函数声明和数据结构定义 |
| avio.c |
无缓冲IO相关函数实现 |
| aviobuf.c |
有缓冲数据IO相关函数实现 |
| cutils.c |
简单的字符串操作函数 |
| file.c |
文件io相关函数 |
| avi.c |
AVI格式的相关函数实现 |
| avidec.c |
AVI格式DEMUXER相关函数定义 |
| avienc.c |
AVI格式MUXER相关函数定义 |
| … |
其他媒体格式的muxer/demuxer的定义实现 |
libavutil
- 1、
主要存放ffmpeg工具类函数的定义
- 2、文件
| 文件 |
功能简介 |
| avutil.h |
简单的像素格式宏定义 |
| bswap.h |
大小端转换函数的实现 |
| common.h |
公共的宏定义和简单函数的实现 |
| … |
其他算法的实现和定义 |
IO模块分析
1、概述
ffmpeg 项目的数据 IO 部分主要是在 libavformat 库中实现,某些对于内存的操作部分在 libavutil库中。
数据 IO 是基于文件格式(Format)以及文件传输协议(Protocol)的,与具体的编解码标准无关
2、相关数据结构介绍
在 libavformat 库中与数据 IO 相关的数据结构主要有 URLProtocol、URLContext、ByteIOContext、AVFormatContext等,之间关系
看图说话
URLProtocol
typedef struct URLProtocol {
const char *name;
int (*url_open)( URLContext *h, const char *url, int flags);
int (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);
int (*url_accept)(URLContext *s, URLContext **c);
int (*url_handshake)(URLContext *c);
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);
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 (*url_get_multi_file_handle)(URLContext *h, int **handles,
int *numhandles);
int (*url_get_short_seek)(URLContext *h);
int (*url_shutdown)(URLContext *h, int flags);
int priv_data_size;
const AVClass *priv_data_class;
int flags;
int (*url_check)(URLContext *h, int mask);
int (*url_open_dir)(URLContext *h);
int (*url_read_dir)(URLContext *h, AVIODirEntry **next);
int (*url_close_dir)(URLContext *h);
int (*url_delete)(URLContext *h);
int (*url_move)(URLContext *h_src, URLContext *h_dst);
const char *default_whitelist;
} URLProtocol;
URLProtocol 是一个链表结构,这是为了协议的统一管理,ffmpeg 项目中
将所有的用到的协议都存放在一个全局变量 first_protocol 中,协议的注册是在
av_register_all 中完成的,新添加单个协议可以调用 av_register_protocol2 函数实
现。而协议的注册就是将具体的协议对象添加至 first_protocol 链表的末尾。
URLContext
typedef struct URLContext {
const AVClass *av_class;
const struct URLProtocol *prot;
void *priv_data;
char *filename;
int flags;
int max_packet_size;
int is_streamed;
int is_connected;
AVIOInterruptCB interrupt_callback;
int64_t rw_timeout;
const char *protocol_whitelist;
const char *protocol_blacklist;
int min_packet_size;
} URLContext;
==那么 ffmpeg 依据什么信息初始化 URLContext?然后又是如何初始化 URLContext
的呢?==
在打开一个 URL 时,全局函数 ffurl_open 会根据 filename 的前缀信息来确定 URL所使用的具体协议,并为该协议分配好资源,再调用 ffurl_connect 函数打开具体协议,即调用协议的 url_open
AVIOContext
typedef struct AVIOContext {
const AVClass *av_class;
unsigned char *buffer;
int buffer_size;
unsigned char *buf_ptr;
unsigned char *buf_end;
void *opaque;
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 eof_reached;
int write_flag;
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;
int64_t maxsize;
int direct;
int64_t bytes_read;
int seek_count;
int writeout_count;
int orig_buffer_size;
int short_seek_threshold;
const char *protocol_whitelist;
const char *protocol_blacklist;
int (*write_data_type)(void *opaque, uint8_t *buf, int buf_size,
enum AVIODataMarkerType type, int64_t time);
int ignore_boundary_point;
enum AVIODataMarkerType current_type;
int64_t last_time;
int (*short_seek_get)(void *opaque);
int64_t written;
unsigned char *buf_ptr_max;
int min_packet_size;
} AVIOContext;
AVIOContext(即:ByteIOContext)是由 URLProtocol 和 URLContext 结构扩展而
来,也是 ffmpeg 提供给用户的接口,它将以上两种不带缓冲的读取文件抽象为带缓冲的
读取和写入,为用户提供带缓冲的读取和写入操作
结构简单的为用户提供读写容易实现的四个操作,read_packet write_packet
read_pause read_seek,极大的方便了文件的读取,四个函数在加了缓冲机制后被中转
到,URLContext 指向的实际的文件协议读写函数中
在 aviobuf.c 中定义了一系列关于 ByteIOContext 这个结构体的函数
Demuxer/muxer模块分析
ffmpeg 的 demuxer 和 muxer 接口分别在 AVInputFormat 和 AVOutputFormat 两个结
构体中实现,在 av_register_all()函数中将两个结构分别静态初始化为两个链表,保
存在全局变量:first_iformat 和 first_oformat 两个变量中
在 FFmpeg 的文件转换或者打开过程中,首先要做的就是根据传入文件和传出文件的后缀名匹配合适的 demuxer和 muxer,得到合适的信息后保存在 AVFormatContext 中
1、AVInputFormat
该结构被称为 demuxer,是音视频文件的一个解封装器
typedef struct AVInputFormat {
const char *name;
const char *long_name;
int flags;
const char *extensions;
const struct AVCodecTag * const *codec_tag;
const AVClass *priv_class;
const char *mime_type;
struct AVInputFormat *next;
int raw_codec_id;
int priv_data_size;
int (*read_probe)(AVProbeData *);
int (*read_header)(struct AVFormatContext *);
int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);
int (*read_close)(struct AVFormatContext *);
int (*read_seek)(struct AVFormatContext *,
int stream_index, int64_t timestamp, int flags);
int64_t (*read_timestamp)(struct AVFormatContext *s, int stream_index,
int64_t *pos, int64_t pos_limit);
int (*read_play)(struct AVFormatContext *);
int (*read_pause)(struct AVFormatContext *);
int (*read_seek2)(struct AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags);
int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list);
int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
} AVInputFormat;
2、AVOutputFormat
该结构与 AVInputFormat 类似也是在编译时静态初始化,组织为一个链表结构,提
供了多个 muxer 的函数接口
对于不同的文件格式要实现相应的函数接口,这样每一种格式都有一个对应的
muxer,所有的 muxer 都保存在全局变量 first_oformat 中
typedef struct AVOutputFormat {
const char *name;
const char *long_name;
const char *mime_type;
const char *extensions;
enum AVCodecID audio_codec;
enum AVCodecID video_codec;
enum AVCodecID subtitle_codec;
int flags;
const struct AVCodecTag * const *codec_tag;
const AVClass *priv_class;
struct AVOutputFormat *next;
int priv_data_size;
int (*write_header)(struct AVFormatContext *);
int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);
int (*write_trailer)(struct AVFormatContext *);
int (*interleave_packet)(struct AVFormatContext *, AVPacket *out,
AVPacket *in, int flush);
int (*query_codec)(enum AVCodecID id, int std_compliance);
void (*get_output_timestamp)(struct AVFormatContext *s, int stream,
int64_t *dts, int64_t *wall);
int (*control_message)(struct AVFormatContext *s, int type,
void *data, size_t data_size);
int (*write_uncoded_frame)(struct AVFormatContext *, int stream_index,
AVFrame **frame, unsigned flags);
int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list);
int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
enum AVCodecID data_codec;
int (*init)(struct AVFormatContext *);
void (*deinit)(struct AVFormatContext *);
int (*check_bitstream)(struct AVFormatContext *, const AVPacket *pkt);
} AVOutputFormat;
3、AVFormatContext
该结构表示与程序当前运行的文件容器格式使用的上下文,着重于所有文件容器共
有的属性,在运行时动态的确定其值,是 AVInputFormat 和 AVOutputFormat 的载体,但
同一个结构对象只能使 AVInputFormat 和 AVOutputFormat 中的某一个有效
typedef struct AVFormatContext {
const AVClass *av_class;
struct AVInputFormat *iformat;
struct AVOutputFormat *oformat;
void *priv_data;
AVIOContext *pb;
int ctx_flags;
unsigned int nb_streams;
AVStream **streams;
#if FF_API_FORMAT_FILENAME
attribute_deprecated
char filename[1024];
#endif
char *url;
int64_t start_time;
int64_t duration;
int64_t bit_rate;
unsigned int packet_size;
int max_delay;
int flags;
#define AVFMT_FLAG_GENPTS 0x0001
#define AVFMT_FLAG_IGNIDX 0x0002
#define AVFMT_FLAG_NONBLOCK 0x0004
#define AVFMT_FLAG_IGNDTS 0x0008
#define AVFMT_FLAG_NOFILLIN 0x0010
#define AVFMT_FLAG_NOPARSE 0x0020
#define AVFMT_FLAG_NOBUFFER 0x0040
#define AVFMT_FLAG_CUSTOM_IO 0x0080
#define AVFMT_FLAG_DISCARD_CORRUPT 0x0100
#define AVFMT_FLAG_FLUSH_PACKETS 0x0200
#define AVFMT_FLAG_BITEXACT 0x0400
#if FF_API_LAVF_MP4A_LATM
#define AVFMT_FLAG_MP4A_LATM 0x8000
#endif
#define AVFMT_FLAG_SORT_DTS 0x10000
#define AVFMT_FLAG_PRIV_OPT 0x20000
#if FF_API_LAVF_KEEPSIDE_FLAG
#define AVFMT_FLAG_KEEP_SIDE_DATA 0x40000
#endif
#define AVFMT_FLAG_FAST_SEEK 0x80000
#define AVFMT_FLAG_SHORTEST 0x100000
#define AVFMT_FLAG_AUTO_BSF 0x200000
int64_t probesize;
int64_t max_analyze_duration;
const uint8_t *key;
int keylen;
unsigned int nb_programs;
AVProgram **programs;
enum AVCodecID video_codec_id;
enum AVCodecID audio_codec_id;
enum AVCodecID subtitle_codec_id;
unsigned int max_index_size;
unsigned int max_picture_buffer;
unsigned int nb_chapters;
AVChapter **chapters;
AVDictionary *metadata;
int64_t start_time_realtime;
int fps_probe_size;
int error_recognition;
AVIOInterruptCB interrupt_callback;
int debug;
#define FF_FDEBUG_TS 0x0001
int64_t max_interleave_delta;
int strict_std_compliance;
int event_flags;
#define AVFMT_EVENT_FLAG_METADATA_UPDATED 0x0001
int max_ts_probe;
int avoid_negative_ts;
#define AVFMT_AVOID_NEG_TS_AUTO -1
#define AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE 1
#define AVFMT_AVOID_NEG_TS_MAKE_ZERO 2
int ts_id;
int audio_preload;
int max_chunk_duration;
int max_chunk_size;
int use_wallclock_as_timestamps;
int avio_flags;
enum AVDurationEstimationMethod duration_estimation_method;
int64_t skip_initial_bytes;
unsigned int correct_ts_overflow;
int seek2any;
int flush_packets;
int probe_score;
int format_probesize;
char *codec_whitelist;
char *format_whitelist;
AVFormatInternal *internal;
int io_repositioned;
AVCodec *video_codec;
AVCodec *audio_codec;
AVCodec *subtitle_codec;
AVCodec *data_codec;
int metadata_header_padding;
void *opaque;
av_format_control_message control_message_cb;
int64_t output_ts_offset;
uint8_t *dump_separator;
enum AVCodecID data_codec_id;
#if FF_API_OLD_OPEN_CALLBACKS
attribute_deprecated
int (*open_cb)(struct AVFormatContext *s, AVIOContext **p, const char *url, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options);
#endif
char *protocol_whitelist;
int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url,
int flags, AVDictionary **options);
void (*io_close)(struct AVFormatContext *s, AVIOContext *pb);
char *protocol_blacklist;
int max_streams;
int skip_estimate_duration_from_pts;
} AVFormatContext;