【问题标题】:Encoding RGB frames using x264 and AVCodec in C在 C 中使用 x264 和 AVCodec 编码 RGB 帧
【发布时间】:2017-03-17 09:57:12
【问题描述】:

我有从相机流式传输的 RGB24 帧,我想将它们编码为 h264,我发现 AVCodec 和 x264 可以这样做,问题是 x264 默认接受 YUV420 作为输入,所以我写的是一个转换 RGB 帧的程序到 YUV420 。这是由 sws_scale 函数实现的。除了它不满足所需的 FPS 之外,它运行良好,因为转换 (RGB->YUV420) 需要时间。

这就是我设置编码器上下文的方式:

videoStream->id = 0;
vCodecCtx = videoStream->codec;

vCodecCtx->coder_type       = AVMEDIA_TYPE_VIDEO;
vCodecCtx->codec_id         = AV_CODEC_ID_H264;
vCodecCtx->bit_rate         = 400000;
vCodecCtx->width            = Width;
vCodecCtx->height           = Height;
vCodecCtx->time_base.den    = FPS;
vCodecCtx->time_base.num    = 1;
//vCodecCtx->time_base      = (AVRational){1,};
vCodecCtx->gop_size         = 12;
vCodecCtx->max_b_frames     = 1;
vCodecCtx->pix_fmt          = AV_PIX_FMT_YUV420P;

if(formatCtx->oformat->flags & AVFMT_GLOBALHEADER)
    vCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;

av_opt_set(vCodecCtx->priv_data, "preset", "ultrafast", 0);
av_opt_set(vCodecCtx->priv_data, "profile", "baseline", AV_OPT_SEARCH_CHILDREN);

if (avcodec_open2(vCodecCtx, h264Codec, NULL) < 0){
    return 0;
}

当我将 AV_PIX_FMT_YUV420P 更改为 AV_PIX_FMT_RGB24 时,avcodec_open2 将失败。 我读到有一个用于 RGB 的 libx264 版本,称为 libx264rgb,但我什至不知道我是否必须启用此选项来重建 x264 或下载另一个源,或者我必须使用第一个 x264 库以编程方式进行。

问题是如何启用 RGB 作为 libx264 的输入以与 C 中的 libavcodec 一起使用。或者如何使编码或 sws_scale 更快。

编辑:

我是如何构建 ffmpeg 的:

NDK=D:/AndroidDev/android-ndk-r9
PLATFORM=$NDK/platforms/android-18/arch-arm/
PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/windows-x86_64

GENERAL="\
--enable-small \
--enable-cross-compile \
--extra-libs="-lgcc" \
--arch=arm \
--cc=$PREBUILT/bin/arm-linux-androideabi-gcc \
--cross-prefix=$PREBUILT/bin/arm-linux-androideabi- \
--nm=$PREBUILT/bin/arm-linux-androideabi-nm \
--extra-cflags="-I../x264/android/arm/include" \
--extra-ldflags="-L../x264/android/arm/lib" "


MODULES="\
--enable-gpl \
--enable-libx264"

function build_ARMv6
{
  ./configure \
  --target-os=linux \
  --prefix=./android/armeabi \
  ${GENERAL} \
  --sysroot=$PLATFORM \
  --enable-shared \
  --disable-static \
  --extra-cflags=" -O3 -fpic -fasm -Wno-psabi -fno-short-enums -fno-strict-aliasing -finline-limit=300 -mfloat-abi=softfp -mfpu=vfp -marm -march=armv6" \
  --extra-ldflags="-lx264 -Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib -lc -lm -ldl -llog" \
  --enable-zlib \
  ${MODULES} \
  --disable-doc \
  --enable-neon

  make clean
  make
  make install
}

build_ARMv6

echo Android ARMEABI builds finished

我是如何构建 x264 的:

NDK=D:/AndroidDev/android-ndk-r9
PLATFORM=$NDK/platforms/android-18/arch-arm/
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/windows-x86_64
PREFIX=./android/arm

function build_one
{
  ./configure \
  --prefix=$PREFIX \
  --enable-static \
  --enable-pic \
  --host=arm-linux \
  --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
  --sysroot=$PLATFORM

  make clean
  make
  make install
}

build_one

echo Android ARM builds finished

【问题讨论】:

    标签: c ffmpeg rgb libavcodec x264


    【解决方案1】:

    要在 libavcodec 中将 RGB 像素格式(AV_PIX_FMT_BGR0、AV_PIX_FMT_BGR24、AV_PIX_FMT_RGB24)与 libx264 一起使用,您需要:

    1. 使用 ffmpeg 项目中的 libavcodec,而不是 libav 项目中的 libavcodec,因为目前它仅在其中可用;
    2. 确保 libavcodec 是使用 libx264rgb (CONFIG_LIBX264RGB_ENCODER) 编译的,据我了解,如果您使用足够新的 8 位 libx264(使用 --enable-libx264 配置),它将启用;
    3. 使用 avcodec_find_encoder_by_name("libx264rgb") 代替 avcodec_find_encoder(AV_CODEC_ID_H264)。

    【讨论】:

    • 好的,1,2 步骤已经完成,现在我使用 avcodec_find_encoder_by_name("libx264rgb") 而不是 avcodec_find_encoder(AV_CODEC_ID_H264) 获得了编码器,并使用上面相同的代码来设置我的编码器上下文并进行更改pix_fmt 到 AV_PIX_FMT_RGB24 ,但是 avcodec_open2 失败,我尝试了另一个 pix_fmt(AV_PIX_FMT_BGR0,AV_PIX_FMT_BGR24,甚至 AV_PIX_FMT_YUV420P)但 avcodec_open2 仍然失败,返回的错误值为 -22,(我认为这意味着“无效参数”)。
    • 如果我的 x264 足够新,我不确定第二步,8 位是什么意思
    • 你检查过 avcodec_find_encoder_by_name("libx264rgb") 的结果不是 NULL 吗? “足够新”意味着没有像 API 117 之前的 5 年旧版本那样过时的东西。“8 位”意味着支持的位深度,并且可以在 libx264 中从 8 到 10(在编译期间只能同时支持和配置一个位深度)。你自己编译 libx264 和 ffmpeg 还是使用一些预编译的版本?
    • 看起来 ffmpeg 可能无法检测 libx264rgb 如果 x264.h 不在标头搜索路径中,即使它使用 pkgconfig 检测到 libx264 本身也是如此。因此,您需要在编译 ffmpeg 时显式添加 x264.h 的路径,方法是: --extra-cflags="-I/usr/local/include"
    • @nobody555 你说得对,我还得设置额外的--extra-cflags="-I../x264/dist/include" \ --extra-ldflags="-L../x264/dist/lib" \
    【解决方案2】:

    编写自己的 RGB2YUV 解码器。

    从框架中获取像素图并通过您的函数运行它。没有缩放,什么都没有,只有一个 for 循环。

    有一些简单的公式可以将 RGB888 转换为 YCbCr (YUV4:4:4)。

    但 AV/FFMpeg 应该可以轻松为您完成。

    对于 YUV420,您需要获取整个 4:4:4 Y 通道,并使用均值或高斯将每 4 个像素插入 U 和 V 以获得 4:2:0。

    像这样:

    此代码需要 ABC4:4:4 CB 或 Cr 通道并返回其 ABC4:2:0 版本。

    #define uint8 unsigned char
    uint8 *ABC_444_to_420 (uint8 *src, uint8 *dst, int w, int h) {
        int dpos, x, y, pl1, pl2;
        dpos = 0;
        for (x=0; x<h; x+=2) {
            for (y=0; y<w; y+=2) {
                pl1 = x*w+y; pl2 = (x+1)*w+y;
                dst[dpos] = (src[pl1]+src[pl1+1]+src[pl2]+src[pl2+1])>>2;
                dpos += 1;
                }
            }
        return dst;
        }
    

    所以,你从 RGB 中得到 YUV444,然后通过上面的代码分别运行 U 和 V。

    如果您无法从 AV/FFMpeg 中找到将 RGB 转换为 YCbCr 的适当函数,请查看:

    https://msdn.microsoft.com/en-us/library/windows/desktop/dd206750%28v=vs.85%29.aspx

    当然,你也可以把RGB转YCbCr的整个过程放在一个passthrough中,直接得到ABC4:2:0。

    不要直接在 AV/FFMpeg 帧像素上工作,在数组中获取您自己的原始数据。然后您将获得最大速度,然后从结果中构造 AV/FFMpeg 帧。

    【讨论】:

    • 顺便说一句,大多数相机应该能够直接为您提供 YCbCr 格式,因此请留意相机模式。有些相机甚至可以为您提供所需的 YUV12,即 ABC4:2:0。希望如此,那么就不需要转换了。只需将相机设置为适当的模式并将数据推送到编码器即可。
    • 首先,感谢您的帮助。关于转换,我不了解 YUV420 的结构来编写完整的转换函数,我尝试了 link 准备的外部函数,但似乎 sws_scale 很快或几乎相同的速度。
    • 你说得对,大多数相机直接提供 YCbCr 格式,但我使用 opengl 来做一些效果,所以相机将像素直接写入 rgb 的 opengl 纹理。应用效果后,我读取像素从纹理为 rgb 。
    • YCbCr 3 通道/平面的工作原理如下: Y 表示强度/亮度(所谓的亮度通道),您可以说它是彩色图像的灰度表示。 Cb为色度蓝差通道,Cr为色度红差通道。当我们有经过伽马校正的图像(主要是照片)时,YCbCr 是非线性的,Cb 和 Cr 可以称为 U 和 V,因此称为 YUV。那些愚蠢的 YUV 数字代表下采样的比率。在自然照片中没有粗糙的边缘,这意味着相邻像素的颜色非常相似,但亮度略有不同。
    • 使用这个属性,我们可以获得具有所需所有像素的全分辨率 Y,但对多个相邻 Y 像素应用相同的色差(由 Cb 和 Cr 调节)。这不会对图像质量产生太大影响。这就是 YUV420 发生的情况。这意味着对于 4 个 Y 像素,您有 1 个 Cb 和 Cr 定义。所以 Y 是 Cb 或 Cr 的 4 倍。我的代码采用完整的 Y~CbCr 即 YUV444 U 或 V 通道并将其下采样到 420。即它取每 4 个相邻像素的平均值。
    猜你喜欢
    • 2014-05-13
    • 2014-11-25
    • 2014-09-18
    • 2013-05-22
    • 2012-12-17
    • 2014-02-24
    • 2015-03-31
    • 2012-08-28
    • 1970-01-01
    相关资源
    最近更新 更多