Skip to content

Commit

Permalink
libav: Add libx264 support
Browse files Browse the repository at this point in the history
Add support for libx264 as a video codec option for libav.  Currently
the options are set to target a performance level of 1080p30.  The
libx264 codec can be selected from the new "libav-video-codec" command
line argument.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
  • Loading branch information
naushir committed Apr 11, 2023
1 parent ba8d8de commit b90f65a
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 25 deletions.
4 changes: 4 additions & 0 deletions core/video_options.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ struct VideoOptions : public Options
("frames", value<unsigned int>(&frames)->default_value(0),
"Run for the exact number of frames specified. This will override any timeout set.")
#if LIBAV_PRESENT
("libav-video-codec", value<std::string>(&libav_video_codec)->default_value("h264_v4l2m2m"),
"Sets the libav video codec to use. "
"To list available codecs, run the \"ffmpeg -codecs\" command.")
("libav-format", value<std::string>(&libav_format)->default_value(""),
"Sets the libav encoder output format to use. "
"Leave blank to try and deduce this from the filename.\n"
Expand Down Expand Up @@ -96,6 +99,7 @@ struct VideoOptions : public Options
unsigned int intra;
bool inline_headers;
std::string codec;
std::string libav_video_codec;
std::string libav_format;
bool libav_audio;
std::string audio_codec;
Expand Down
85 changes: 60 additions & 25 deletions encoder/libav_encoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

#include "libav_encoder.hpp"

namespace {

void encoderOptionsH264M2M(VideoOptions const *options, AVCodecContext *codec)
{
codec->pix_fmt = AV_PIX_FMT_DRM_PRIME;
Expand All @@ -27,18 +29,44 @@ void encoderOptionsH264M2M(VideoOptions const *options, AVCodecContext *codec)
codec->bit_rate = options->bitrate;
}

static const std::map<std::string, std::function<void(VideoOptions const *, AVCodecContext *)>> optionsMap =
void encoderOptionsLibx264(VideoOptions const *options, AVCodecContext *codec)
{
codec->pix_fmt = AV_PIX_FMT_YUV420P;

if (options->bitrate)
codec->bit_rate = options->bitrate;

codec->max_b_frames = 1;
codec->me_range = 16;
codec->me_cmp = 1; // No chroma ME
codec->me_subpel_quality = 0;
codec->thread_count = 0;
codec->thread_type = FF_THREAD_FRAME;
codec->slices = 1;

av_opt_set(codec->priv_data, "preset", "superfast", 0);
av_opt_set(codec->priv_data, "partitions", "i8x8,i4x4", 0);
av_opt_set(codec->priv_data, "weightp", "none", 0);
av_opt_set(codec->priv_data, "weightb", "0", 0);
av_opt_set(codec->priv_data, "motion-est", "dia", 0);
av_opt_set(codec->priv_data, "sc_threshold", "0", 0);
av_opt_set(codec->priv_data, "rc-lookahead", "0", 0);
av_opt_set(codec->priv_data, "mixed_ref", "0", 0);
}

const std::map<std::string, std::function<void(VideoOptions const *, AVCodecContext *)>> optionsMap =
{
{ "h264_v4l2m2m", encoderOptionsH264M2M },
{ "libx264", encoderOptionsLibx264 },
};

} // namespace

void LibAvEncoder::initVideoCodec(VideoOptions const *options, StreamInfo const &info)
{
const std::string codec_name("h264_v4l2m2m");

const AVCodec *codec = avcodec_find_encoder_by_name(codec_name.c_str());
const AVCodec *codec = avcodec_find_encoder_by_name(options->libav_video_codec.c_str());
if (!codec)
throw std::runtime_error("libav: cannot find video encoder " + codec_name);
throw std::runtime_error("libav: cannot find video encoder " + options->libav_video_codec);

codec_ctx_[Video] = avcodec_alloc_context3(codec);
if (!codec_ctx_[Video])
Expand Down Expand Up @@ -113,7 +141,7 @@ void LibAvEncoder::initVideoCodec(VideoOptions const *options, StreamInfo const
: (int)(options->framerate.value_or(DEFAULT_FRAMERATE));

// Apply any codec specific options:
auto fn = optionsMap.find("h264_v4l2m2m");
auto fn = optionsMap.find(options->libav_video_codec);
if (fn != optionsMap.end())
fn->second(options, codec_ctx_[Video]);

Expand Down Expand Up @@ -309,32 +337,39 @@ void LibAvEncoder::EncodeBuffer(int fd, size_t size, void *mem, StreamInfo const
frame->linesize[1] = frame->linesize[2] = info.stride >> 1;
frame->pts = timestamp_us - video_start_ts_ + (options_->av_sync < 0 ? -options_->av_sync : 0);

if (codec_ctx_[Video]->pix_fmt == AV_PIX_FMT_DRM_PRIME)
{
std::scoped_lock<std::mutex> lock(drm_queue_lock_);
drm_frame_queue_.emplace(std::make_unique<AVDRMFrameDescriptor>());
frame->buf[0] = av_buffer_create((uint8_t *)drm_frame_queue_.back().get(), sizeof(AVDRMFrameDescriptor),
&LibAvEncoder::releaseBuffer, this, 0);
frame->data[0] = frame->buf[0]->data;
}

AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)frame->data[0];
desc->nb_objects = 1;
desc->objects[0].fd = fd;
desc->objects[0].size = size;
desc->objects[0].format_modifier = DRM_FORMAT_MOD_INVALID;

desc->nb_layers = 1;
desc->layers[0].format = DRM_FORMAT_YUV420;
desc->layers[0].nb_planes = 3;
desc->layers[0].planes[0].object_index = 0;
desc->layers[0].planes[0].offset = 0;
desc->layers[0].planes[0].pitch = info.stride;
desc->layers[0].planes[1].object_index = 0;
desc->layers[0].planes[1].offset = info.stride * info.height;
desc->layers[0].planes[1].pitch = info.stride >> 1;
desc->layers[0].planes[2].object_index = 0;
desc->layers[0].planes[2].offset = info.stride * info.height * 5 / 4;
desc->layers[0].planes[2].pitch = info.stride >> 1;
AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)frame->data[0];
desc->nb_objects = 1;
desc->objects[0].fd = fd;
desc->objects[0].size = size;
desc->objects[0].format_modifier = DRM_FORMAT_MOD_INVALID;

desc->nb_layers = 1;
desc->layers[0].format = DRM_FORMAT_YUV420;
desc->layers[0].nb_planes = 3;
desc->layers[0].planes[0].object_index = 0;
desc->layers[0].planes[0].offset = 0;
desc->layers[0].planes[0].pitch = info.stride;
desc->layers[0].planes[1].object_index = 0;
desc->layers[0].planes[1].offset = info.stride * info.height;
desc->layers[0].planes[1].pitch = info.stride >> 1;
desc->layers[0].planes[2].object_index = 0;
desc->layers[0].planes[2].offset = info.stride * info.height * 5 / 4;
desc->layers[0].planes[2].pitch = info.stride >> 1;
}
else
{
frame->buf[0] = av_buffer_create((uint8_t *)mem, size, &LibAvEncoder::releaseBuffer, this, 0);
av_image_fill_pointers(frame->data, AV_PIX_FMT_YUV420P, frame->height, frame->buf[0]->data, frame->linesize);
av_frame_make_writable(frame);
}

std::scoped_lock<std::mutex> lock(video_mutex_);
frame_queue_.push(frame);
Expand Down
1 change: 1 addition & 0 deletions encoder/libav_encoder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ extern "C"
#include "libavutil/audio_fifo.h"
#include "libavutil/hwcontext.h"
#include "libavutil/hwcontext_drm.h"
#include "libavutil/imgutils.h"
#include "libavutil/timestamp.h"
#include "libavutil/version.h"
#include "libswresample/swresample.h"
Expand Down

0 comments on commit b90f65a

Please sign in to comment.