在 C 中使用 x264 和 AVCodec 编码 RGB 帧
Encoding RGB frames using x264 and AVCodec in C
我有从相机流出的 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
要在 libavcodec 中将 RGB 像素格式(AV_PIX_FMT_BGR0、AV_PIX_FMT_BGR24、AV_PIX_FMT_RGB24)与 libx264 一起使用,您需要:
- 使用 ffmpeg 项目的 libavcodec 而不是 libav 项目,因为目前它只在那里可用;
- 确保 libavcodec 是使用 libx264rgb (CONFIG_LIBX264RGB_ENCODER) 编译的,据我所知,如果您使用足够新的 8 位 libx264(使用 --enable-libx264 配置),它将被启用;
- 使用 avcodec_find_encoder_by_name("libx264rgb") 而不是 avcodec_find_encoder(AV_CODEC_ID_H264).
编写自己的 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 通道和 returns 它的 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 帧。
我有从相机流出的 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
要在 libavcodec 中将 RGB 像素格式(AV_PIX_FMT_BGR0、AV_PIX_FMT_BGR24、AV_PIX_FMT_RGB24)与 libx264 一起使用,您需要:
- 使用 ffmpeg 项目的 libavcodec 而不是 libav 项目,因为目前它只在那里可用;
- 确保 libavcodec 是使用 libx264rgb (CONFIG_LIBX264RGB_ENCODER) 编译的,据我所知,如果您使用足够新的 8 位 libx264(使用 --enable-libx264 配置),它将被启用;
- 使用 avcodec_find_encoder_by_name("libx264rgb") 而不是 avcodec_find_encoder(AV_CODEC_ID_H264).
编写自己的 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 通道和 returns 它的 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 帧。