从国外git仓库下载ffmeg4.3源码速度比较慢,推荐使用国内gitee镜像下载。
git clone https://gitee.com/mirrors/ffmpeg.git -b release/4.3
./configure --enable-shared --prefix=/home/zhy/code/mypc/ffmpeg4.3/ffmpeg/install_lib
make
make install
编译后会生成ffmpeg、ffprobe等可执行文件,执行ffmeg查看是否编译成功:
./ffmpeg -version
执行命令后会报so找不到的错误:
ffmpeg: error while loading shared libraries: libavdevice.so.58: cannot open shared object file: No such file or directory
原因未进行环境变量配置,找不到so链接路径,需要执行以下操作:
sudo vim /etc/ld.so.conf
在conf中添加prefix中指定的lib install 路径,添加后入下:
include /etc/ld.so.conf.d/*.conf
/home/zhy/code/mypc/ffmpeg4.3/ffmpeg/install_lib/lib
最后更新一下配置:
sudo ldconfig
再执行./ffmpeg -version命令就可正常了。
zhy@zhy-ThinkPad-E480:~/code/mypc/ffmpeg4.3/ffmpeg$ ./ffmpeg -version
ffmpeg version n4.3.2-168-g3aba8b176f Copyright (c) 2000-2021 the FFmpeg developers
built with gcc 7 (Ubuntu 7.5.0-3ubuntu1~18.04)
configuration: --enable-shared --prefix=/home/zhy/code/mypc/ffmpeg4.3/ffmpeg/install_lib
libavutil 56. 51.100 / 56. 51.100
libavcodec 58. 91.100 / 58. 91.100
libavformat 58. 45.100 / 58. 45.100
libavdevice 58. 10.100 / 58. 10.100
libavfilter 7. 85.100 / 7. 85.100
libswscale 5. 7.100 / 5. 7.100
libswresample 3. 7.100 / 3. 7.100
zhy@zhy-ThinkPad-E480:~/code/mypc/ffmpeg4.3/ffmpeg$
ffmpeg中一个dumuxer需要包含read_header、read_packet等基本函数,如mpegts demuxer如下所示:
AVInputFormat ff_mpegtsraw_demuxer = {
.name = "mpegtsraw",
.long_name = NULL_IF_CONFIG_SMALL("raw MPEG-TS (MPEG-2 Transport Stream)"),
.priv_data_size = sizeof(MpegTSContext),
.read_header = mpegts_read_header,
.read_packet = mpegts_raw_read_packet,
.read_close = mpegts_read_close,
.read_timestamp = mpegts_get_dts,
.flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT,
.priv_class = &mpegtsraw_class,
};
一般来说audio和video的demuxer比较复杂,所以选择添加一个image的demuxer,image的格式选择了webp,webp 格式的spec见参考文章。
在libavformat\rawdec.h中添加ff_webp_demuxer定义:
#define FF_DEF_RAWIMAGE_DEMUXER(shortname, longname, probe, ext, id, flag)\
FF_RAW_DEMUXER_CLASS(shortname)\
AVInputFormat ff_ ## shortname ## _demuxer = {\
.name = #shortname,\
.long_name = NULL_IF_CONFIG_SMALL(longname),\
.read_probe = probe,\
.read_header = ff_raw_image_read_header,\
.read_packet = ff_raw_image_read_packet,\
.extensions = ext,\
.flags = flag,\
.raw_codec_id = id,\
.priv_data_size = sizeof(FFRawVideoDemuxerContext),\
.priv_class = &shortname ## _demuxer_class,\
};
在libavformat\rawdec.c中添加实现相关函数ff_raw_image_read_header、ff_raw_image_read_packet函数以及webp 的probe函数。
probe函数主要是用来推测container的fmt,webp格式的图片主要通过"RIFF"和”WEBP“两个推测,probe函数如下所示:
static int webp_probe(const AVProbeData *p)
{
uint8_t *b = p->buf;
printf("[debug] webp_probe\n");
if (AV_RB32(b) == MKBETAG('R', 'I', 'F', 'F')
&& AV_RB32(b + 8) == MKBETAG('W', 'E', 'B', 'P'))
{
printf("[debug] is webp image \n");
return AVPROBE_SCORE_MAX - 1;
}
return 0;
}
ff_raw_image_read_packet函数不需要特别处理,每次读取一定size的buffer就可以了。
int ff_raw_image_read_packet(AVFormatContext *s, AVPacket *pkt)
{
int ret, size = 1024;
ret = av_get_packet(s->pb, pkt, size);
if (ret < 0) {
return ret;
}
pkt->size = ret;
pkt->stream_index = 0;
pkt->flags = AV_PKT_FLAG_KEY;
return ret;
}
read_header函数中一般读取图片的width、height以及一些flag信息,ff_raw_image_read_header函数实现如下:
int ff_raw_image_read_header(AVFormatContext *s)
{
AVStream *st;
FFRawVideoDemuxerContext *s1 = s->priv_data;
int ret = 0;
printf("[debug] %s: %d\n", __func__, __LINE__);
st = avformat_new_stream(s, NULL);
if (!st) {
ret = AVERROR(ENOMEM);
goto fail;
}
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->codec_id = s->iformat->raw_codec_id;
st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
if (AV_CODEC_ID_WEBP == st->codecpar->codec_id)
{
webp_read_info(s, st);
}
fail:
return ret;
}
其中webp_read_info主要是根据webp container提取了一些信息,webp container最常用的结构入下所示,详细内容见参考文章中webp官方文档:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| WebP file header (12 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ChunkHeader('VP8X') |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Rsv|I|L|E|X|A|R| Reserved |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Canvas Width Minus One | ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
... Canvas Height Minus One |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
其中, WebP file header如下:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 'R' | 'I' | 'F' | 'F' |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| File Size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 'W' | 'E' | 'B' | 'P' |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
根据以上结构,webp_read_info中内容如下所示:
static int webp_read_info (AVFormatContext *s ,AVStream *st)
{
#define VP8X_FLAG_ANIMATION 0x02
#define VP8X_FLAG_XMP_METADATA 0x04
#define VP8X_FLAG_EXIF_METADATA 0x08
#define VP8X_FLAG_ALPHA 0x10
#define VP8X_FLAG_ICC 0x20
int ret = 0;
int len = 0;
int64_t pos = 0;
int64_t cur_pos = 0;
AVIOContext *pb = s->pb;
int flag = 0;
printf("[debug] %s: %d\n", __func__, __LINE__);
if (NULL == pb)
{
printf("[error] %s: %d invalid input \n", __func__, __LINE__);
goto END;
}
pos = avio_tell(pb);
avio_seek(pb, 0, SEEK_SET);
/*read RIFF tag*/
ret = avio_rb32(pb);
if (ret == MKBETAG('R', 'I', 'F', 'F'))
{
printf("[debug] read RIFF tag \n");
}
else
{
goto END;
}
/*read size*/
ret = avio_rb32(pb);
/*read WEBP tag*/
ret = avio_rb32(pb);
if (ret == MKBETAG('W', 'E', 'B', 'P'))
{
printf("[debug] read WEBP tag \n");
}
/*read VP8X tag*/
ret = avio_rb32(pb);
if (ret == MKBETAG('V', 'P', '8', 'X'))
{
printf("[debug] read VP8X tag \n");
}
/*read size*/
ret = avio_rb32(pb);
/*read flag*/
flag = avio_r8(pb);
printf(" ICCP: %d\n Alpha: %d\n EXIF: %d\n XMP: %d\n Animation: %d\n",
(flag & VP8X_FLAG_ICC) != 0,
(flag & VP8X_FLAG_ALPHA) != 0,
(flag & VP8X_FLAG_EXIF_METADATA) != 0,
(flag & VP8X_FLAG_XMP_METADATA) != 0,
(flag & VP8X_FLAG_ANIMATION) != 0);
avio_seek(pb, pos, SEEK_SET);
END:
return ret;
}
主要读取了VP8X Chunk中的一些flag,更详细的webp demux可以参考libwebp实现。
ffmpeg中原本有webp 的demuxer ,因此需要在libavformat\allformats.c注释之前的ff_image_webp_pipe_demuxer并添加自定义ff_webp_demuxer
/* image demuxers */
//extern AVInputFormat ff_image_webp_pipe_demuxer;
extern AVInputFormat ff_image_xpm_pipe_demuxer;
extern AVInputFormat ff_image_xwd_pipe_demuxer;
extern AVInputFormat ff_webp_demuxer;/*new add*/
注释掉ff_image_webp_pipe_demuxer编译不过只需要把ff_image_webp_pipe_demuxer调用的地方干掉就可以了,最后还需要在 config.h关闭自带webp demuxer,设置为0。
#define CONFIG_IMAGE_WEBP_PIPE_DEMUXER 0
重新编译并install,测试ffprobe:
./ffprobe samples/webp/animation/animated-webp-supported.webp
运行结果:
zhy@zhy-ThinkPad-E480:~/code/mypc/ffmpeg4.3/ffmpeg$ ./ffprobe samples/webp/animation/animated-webp-supported.webp
ffprobe version n4.3.2-168-g3aba8b176f Copyright (c) 2007-2021 the FFmpeg developers
built with gcc 7 (Ubuntu 7.5.0-3ubuntu1~18.04)
configuration: --enable-shared --prefix=/home/zhy/code/mypc/ffmpeg4.3/ffmpeg/install_lib
libavutil 56. 51.100 / 56. 51.100
libavcodec 58. 91.100 / 58. 91.100
libavformat 58. 45.100 / 58. 45.100
libavdevice 58. 10.100 / 58. 10.100
libavfilter 7. 85.100 / 7. 85.100
libswscale 5. 7.100 / 5. 7.100
libswresample 3. 7.100 / 3. 7.100
[debug] webp_probe
[debug] is webp image
[debug] ff_raw_image_read_header: 338
[debug] webp_read_info: 251
[debug] read RIFF tag
[debug] read WEBP tag
[debug] read VP8X tag
ICCP: 0
Alpha: 1
EXIF: 0
XMP: 0
Animation: 1
[webp @ 0x5618e1c420c0] skipping unsupported chunk: ANIM
[webp @ 0x5618e1c420c0] skipping unsupported chunk: ANMF
Last message repeated 11 times
[webp @ 0x5618e1c420c0] image data not found
[webp @ 0x5618e1c40d80] decoding for stream 0 failed
[webp @ 0x5618e1c40d80] Could not find codec parameters for stream 0 (Video: webp, none): unspecified size
Consider increasing the value for the 'analyzeduration' and 'probesize' options
Input #0, webp, from 'samples/webp/animation/animated-webp-supported.webp':
Duration: N/A, bitrate: N/A
Stream #0:0: Video: webp, none, 90k tbr, 90k tbn, 90k tbc
zhy@zhy-ThinkPad-E480:~/code/mypc/ffmpeg4.3/ffmpeg$
libwebp中webpinfo读取结果:
zhy@zhy-ThinkPad-E480:~/code/mypc/mutil_media/libwebp/examples$ ./webpinfo samples/webp/animation/animated-webp-supported.webp
File: samples/webp/animation/animated-webp-supported.webp
RIFF HEADER:
File size: 37342
Chunk VP8X at offset 12, length 18
[leif] webp_info->feature_flags_ = 18
ICCP: 0
Alpha: 1
EXIF: 0
XMP: 0
Animation: 1
Canvas size 400 x 400
可以看出,读取的flag与使用libwebp读取的一致,证明添加的自定义webp的demuxer 功能OK。
1 https://developers.google.com/speed/webp/docs/riff_container