前言

Kdenlive是一款由KDE(知名的Linux社区,为Linux桌面生态提供了强有力的支持)研发的视音频处理软件,它集成了音频和视频的处理及创造功能,并且提供了字幕工具、DVD创作等视音频解决方案,让使用Kdenlive的人员可以方便快速的完成视音频的创作工作。

使用Kdenlive软件对视频进行创作大致可以分成3个步骤,第一步是将待进行制作的视频加载进入软件,第二步是对视频进行创作,最后一步就是将创作好的视频导出,完成这三步之后就可以获得期望的视频了。

在视频导出时,需要用到多媒体编码技术,该技术是影响视频导出速度的关键,考虑到制作视频已经花费了很多的精力和时间,所以应该没有人希望视频导出还有花费大量的时间,而选择一种合适的多媒体编码技术可以有效降低视频导出的耗时。

本文将会对幽兰代码本上使用Kdenlive软件导出视频时,占用时间过多的问题进行探讨。

多媒体编码技术

多媒体编码技术可以分成硬件编解码码和软件编解码码两类,它们区别在于软件编解码使用CPU进行编码,需要消耗大量的CPU资源,而硬件编解码则使用非CPU(如GPU)进行编解码,消耗的CPU资源会更少。

一般来说,硬件编码生成视频的速度会快于软件编码,因此出于节省时间的考虑,使用硬件编码技术往往一种更加明智的选择。

瑞芯微平台的硬件编码方案

幽兰代码本使用的是瑞芯微设计的RK3588处理器,瑞芯微提供了媒体处理软件平台(简称:MPP)作为其旗下芯片的多媒体软件处理平台,MPP的作用是与Linux编解码驱动进行交互,操作瑞芯片平台提供的硬件加速模块,并给期望使用硬件编码技术的软件开发人员提供统一的视音频媒体处理接口(简称:MPI),协助视音频软件开发者使用硬件加速资源。

由于MPP需要Linux内核驱动的支持,所以在编译内核时需要添加下面的编译选项。

CONFIG_ROCKCHIP_MPP_SERVICE=y
CONFIG_ROCKCHIP_MPP_PROC_FS=y
CONFIG_ROCKCHIP_MPP_RKVDEC=y
CONFIG_ROCKCHIP_MPP_RKVDEC2=y
CONFIG_ROCKCHIP_MPP_RKVENC=y
CONFIG_ROCKCHIP_MPP_RKVENC2=y
CONFIG_ROCKCHIP_MPP_VDPU1=y
CONFIG_ROCKCHIP_MPP_VEPU1=y
CONFIG_ROCKCHIP_MPP_VDPU2=y
CONFIG_ROCKCHIP_MPP_VEPU2=y
CONFIG_ROCKCHIP_MPP_IEP2=y
CONFIG_ROCKCHIP_MPP_JPGDEC=y
CONFIG_ROCKCHIP_MPP_AV1DEC=y

在用户空间中,MPP通过虚文件与驱动进行交互。

/dev/mpp_service

Kdenlive

谁在导出视频?

Kdenlive对视频进行导出时,可以观察kdenlive的相关进程。

geduer    169924    2148 10 14:29 ?        00:00:31 /usr/bin/kdenlive
geduer    170093     985  0 14:30 ?        00:00:00 /usr/bin/kdenlive_render /usr/bin/melt-7 /tmp/kdenlive-UUciLc.mlt /home/geduer/Videos/?????????.mp4 -pid:169924 -out 150993

此时发现kdenlive使用kdenlive_render程序对视频进行导出。

再继续观察kdenlive_render进程,通过进程的命令行可以知道,kdenlive_render自身并不进行编码操作,而是将编码任务交给melt-7程序完成。

melt-7kdenlive都是基于MLT开源多媒体框架进行开发的视音频处理程序,由于kdenlive本身并没有加入视频导出的功能,因此选择借助melt-7补齐这一功能。

使用何种编码技术?

想要使用MPP进行加速,就需要使用对应的动态链接库,然后调用对应的接口。

mpp为关键字检索幽兰代码本上的文件可以找到librockchip_mpp.so.0,它就是MPP的代码实现。

librokchip_mpp为关键字检索melt-7对应的maps文件,可以看到melt-7的进程空间中已经加载了MPP

7f81a80000-7f81c47000 r-xp 00000000 07:00 806594                         /usr/local/lib/librockchip_mpp.so.0
7f81c47000-7f81c5c000 ---p 001c7000 07:00 806594                         /usr/local/lib/librockchip_mpp.so.0
7f81c5c000-7f81c60000 r--p 001cc000 07:00 806594                         /usr/local/lib/librockchip_mpp.so.0
7f81c60000-7f81c6f000 rw-p 001d0000 07:00 806594                         /usr/local/lib/librockchip_mpp.so.0

但是melt-7真的使用MPP提供的功能了吗?还是因为由于其他动态链接库依赖MPP,导致librockchip_mpp.so.0也一并被加载进来了呢?目前都是不可知的,所以还需要带着这个疑问继续深入探查。

通过观看melt-7对应的maps文件可以看到,melt-7进程使用了需要MLT框架相关的动态链接库,在这里面可以看到带有av字样的相关库,其中av代表的是audiovideo

7f8c0d0000-7f8c0ee000 r-xp 00000000 07:00 4463634                        /usr/lib/aarch64-linux-gnu/mlt-7/libmltavformat.so
7f8c0ee000-7f8c0ff000 ---p 0001e000 07:00 4463634                        /usr/lib/aarch64-linux-gnu/mlt-7/libmltavformat.so
7f8c0ff000-7f8c100000 r--p 0001f000 07:00 4463634                        /usr/lib/aarch64-linux-gnu/mlt-7/libmltavformat.so
7f8c100000-7f8c101000 rw-p 00020000 07:00 4463634                        /usr/lib/aarch64-linux-gnu/mlt-7/libmltavformat.so

通过名字可以推断libmltavformat.soMLT框架实现视音频处理的核心部分。

观察libmltavformat.so的依赖可以发现动态链接库libavcodec.so,其中codec的中文含义就是编解码器。

libavcodec.so.59 => /lib/aarch64-linux-gnu/libavcodec.so.59 (0x0000007fb3490000)

libavcodec是一款应用广泛的编解码引擎,不少软件通过该引擎完成视音频的编解码工作。显然MLT框架也使用了该引擎进行编解码的任务。

借助perf追踪melt-7进程的执行细节可以发现,进行正在使用libavcodec.so

libx264.so.164 => /lib/aarch64-linux-gnu/libx264.so.164 (0x0000007fabcb0000)

此时libavcodec库内部进行的操作已经不能直接感知到了,因此这里选择使用perf继续追踪melt-7进程在运行时的函数调用过程。

libx264.so.164            [.] slicetype_mb_cost
libswscale.so.6.7.100     [.] yuv2yuyv422_2_c
libswscale.so.6.7.100     [.] ff_hscale8to15_4_neon
libc.so.6                 [.] __GI_memcpy
libswscale.so.6.7.100     [.] yuyvtoyuv420_c
libx264.so.164            [.] 0x0000000000107164
libx264.so.164            [.] 0x0000007f7cfc7164
libx264.so.164            [.] 0x0000000000107168
libx264.so.164            [.] 0x0000007f7cfc7168
libx264.so.164            [.] 0x0000000000107170
libx264.so.164            [.] 0x0000007f7cfc7170
libx264.so.164            [.] 0x0000000000107174
libx264.so.164            [.] 0x0000007f7cfc7174
libavcodec.so.59.37.100   [.] ff_h264_decode_mb_cabac
libmlt-7.so.7.12.0        [.] mlt_cache_put_frame
libavcodec.so.59.37.100   [.] ff_put_h264_chroma_mc8_neon
libavcodec.so.59.37.100   [.] loop_filter

通过观察melt-7进程在运行过程中函数调用可以发现libavcodeclibx264两个库。

libavcodec库中支持许多的编解码技术,其中有如libx264一样的软件编解码技术,也有如MPP一样的硬件编解码技术。

显然此处并没有使用瑞芯微提供MPP进行硬件加速,这也是Kdenlive导出视频速度较慢的原因。

尚待完善的瑞芯片平台

在了解libavcodec使用libx264提供的软件编解码技术后,需要进一步了解libavcodec为什么没有使用瑞芯微提供的MPP硬件编解码技术。

在上面可以看到此时melt-7程序使用的是59版本,而最新的版本则是60,通过查看59版本和60版本的源代码可以知道,不管是59版本还是60版本,都是只支持MPP提供的硬件解码技术,而不支持硬件编码技术。

所以通过libavcodec编解码因为是不能使用MPP的编码功能的。

下面是libavcodec中通过MPP进行解码的部分代码。

static int rkmpp_write_data(AVCodecContext *avctx, uint8_t *buffer, int size, int64_t pts)
{
    RKMPPDecodeContext *rk_context = avctx->priv_data;
    RKMPPDecoder *decoder = (RKMPPDecoder *)rk_context->decoder_ref->data;
    int ret;
    MppPacket packet;

    // create the MPP packet
    ret = mpp_packet_init(&packet, buffer, size);
    if (ret != MPP_OK) {
        av_log(avctx, AV_LOG_ERROR, "Failed to init MPP packet (code = %d)\n", ret);
        return AVERROR_UNKNOWN;
    }

    mpp_packet_set_pts(packet, pts);

    if (!buffer)
        mpp_packet_set_eos(packet);

    ret = decoder->mpi->decode_put_packet(decoder->ctx, packet);
    if (ret != MPP_OK) {
        if (ret == MPP_ERR_BUFFER_FULL) {
            av_log(avctx, AV_LOG_DEBUG, "Buffer full writing %d bytes to decoder\n", size);
            ret = AVERROR(EAGAIN);
        } else
            ret = AVERROR_UNKNOWN;
    }
    else
        av_log(avctx, AV_LOG_DEBUG, "Wrote %d bytes to decoder\n", size);

    mpp_packet_deinit(&packet);

    return ret;
}

尽管瑞芯微已经提供了MPP的硬件加速方案来支持软件,但是不少著名且常用的软件对应瑞芯微平台的支持仍然是比较有限的。

“生态环境”的问题仍需要瑞芯微努力推动。

作者:admin  创建时间:2024-03-06 11:52
最后编辑:admin  更新时间:2024-05-06 17:42