使用x中的x264和AVCodec对RGB帧进行编码

时间:2016-11-03 17:34:04

标签: c ffmpeg rgb libavcodec x264

我有从相机流式传输的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将失败。 我读到有一个名为libx264rgb的用于RGB的libx264版本,但我甚至不知道我是否必须通过启用此选项来重建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

2 个答案:

答案 0 :(得分:1)

要在libavcodec中使用RGB像素格式(AV_PIX_FMT_BGR0,AV_PIX_FMT_BGR24,AV_PIX_FMT_RGB24)和libx264,您需要:

  1. 使用来自ffmpeg项目的libavcodec,而不是来自libav项目,因为目前它只在那里可用;
  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 :(得分:0)

编写自己的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转换中将整个过程放入一个直通中,直接输出ABC4:2:0。

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