ffmpeg 4.3添加自定义demuxer

1 编译ffmpeg4.3

1.1 ffmpeg 4.3下载

从国外git仓库下载ffmeg4.3源码速度比较慢,推荐使用国内gitee镜像下载。

git clone https://gitee.com/mirrors/ffmpeg.git -b release/4.3 

1.2 编译代码

./configure --enable-shared --prefix=/home/zhy/code/mypc/ffmpeg4.3/ffmpeg/install_lib
make
make install

1.3 环境变量配置

编译后会生成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$ 

2 添加自定义demuxer

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见参考文章。

2.1 添加demuxer 定义

在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,\
};

2.2 添加demuxer 定义

在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实现。

2.3 更换ffmpeg中的webp demuxer

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

2.4 运行结果

重新编译并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

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值