Skip to content

Commit

Permalink
enc: set hw frame context's sw format. vaapi works
Browse files Browse the repository at this point in the history
  • Loading branch information
wang-bin committed Aug 28, 2016
1 parent d888ad7 commit a0e58de
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 25 deletions.
8 changes: 7 additions & 1 deletion examples/simpletranscode/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ int main(int argc, char *argv[])
{
QApplication a(argc, argv);
qDebug("QtAV simpletranscode");
qDebug("./simpletranscode -i infile -o outfile [-async] [-c:v video_codec (default: libx264)] [-f format] [-an] [-ss HH:mm:ss.z]");
qDebug("./simpletranscode -i infile -o outfile [-async] [-c:v video_codec (default: libx264)] [-hwdev dev] [-f format] [-an] [-ss HH:mm:ss.z]");
qDebug("-an: disable audio");
qDebug() << "examples:\n"
<< "./simpletranscode -i test.mp4 -o /tmp/test-%05d.png -f image2 -c:v png\n"
Expand Down Expand Up @@ -62,6 +62,10 @@ int main(int argc, char *argv[])
idx = a.arguments().indexOf(QLatin1String("-f"));
if (idx > 0)
fmt = a.arguments().at(idx + 1);
QString hwdev;
idx = a.arguments().indexOf(QLatin1String("-hwdev"));
if (idx > 0)
hwdev = a.arguments().at(idx + 1);
const bool an = a.arguments().contains(QLatin1String("-an"));
const bool async = a.arguments().contains(QLatin1String("-async"));
qint64 ss = 0;
Expand Down Expand Up @@ -94,6 +98,8 @@ int main(int argc, char *argv[])
VideoEncoder *venc = avt.videoEncoder();
venc->setCodecName(cv); // "png"
venc->setBitRate(1024*1024);
if (!hwdev.isEmpty())
venc->setProperty("hwdevice", hwdev);
if (fmt == QLatin1String("image2"))
venc->setPixelFormat(VideoFormat::Format_RGBA32);
if (an) {
Expand Down
67 changes: 43 additions & 24 deletions src/codec/video/VideoEncoderFFmpeg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@

#if AV_MODULE_CHECK(LIBAVUTIL, 55, 13, 0, 27, 100)
#define HAVE_AVHWCTX
extern "C" {
#include <libavutil/hwcontext.h>
}
#endif

/*!
Expand Down Expand Up @@ -64,15 +66,18 @@ AVHWDeviceType fromHWAName(const char* name)
class VideoEncoderFFmpegPrivate;
class VideoEncoderFFmpeg Q_DECL_FINAL: public VideoEncoder
{
Q_OBJECT
DPTR_DECLARE_PRIVATE(VideoEncoderFFmpeg)
Q_PROPERTY(QString hwdevice READ hwDevice WRITE setHWDevice)
Q_PROPERTY(QString hwdevice READ hwDevice WRITE setHWDevice NOTIFY hwDeviceChanged)
public:
VideoEncoderFFmpeg();
VideoEncoderId id() const Q_DECL_OVERRIDE;
bool encode(const VideoFrame &frame = VideoFrame()) Q_DECL_OVERRIDE;

void setHWDevice(const QString& name);
QString hwDevice() const;
Q_SIGNALS:
void hwDeviceChanged();
};

static const VideoEncoderId VideoEncoderId_FFmpeg = mkid::id32base36_6<'F', 'F', 'm', 'p', 'e', 'g'>::value;
Expand Down Expand Up @@ -138,16 +143,16 @@ bool VideoEncoderFFmpegPrivate::open()
if (codec->pix_fmts) {
qDebug("use first supported pixel format: %d", codec->pix_fmts[0]);
format_used = VideoFormat::pixelFormatFromFFmpeg((int)codec->pix_fmts[0]);
if (av_pix_fmt_desc_get(codec->pix_fmts[0])->flags & AV_PIX_FMT_FLAG_HWACCEL) {
hwfmt = codec->pix_fmts[0];
}
} else {
qWarning("pixel format and supported pixel format are not set. use yuv420p");
format_used = VideoFormat::Format_YUV420P;
}
}
//avctx->sample_aspect_ratio =
if (av_pix_fmt_desc_get(codec->pix_fmts[0])->flags & AV_PIX_FMT_FLAG_HWACCEL)
hwfmt = codec->pix_fmts[0];
if (hwfmt != AVPixelFormat(-1)) {
#ifdef HAVE_AVHWCTX
avctx->pix_fmt = hwfmt;
hw_device_ctx = NULL;
AV_ENSURE(av_hwdevice_ctx_create(&hw_device_ctx, fromHWAName(codec_name.section(QChar('_'), -1).toUtf8().constData()), hwdev.toLatin1().constData(), NULL, 0), false);
Expand All @@ -156,29 +161,30 @@ bool VideoEncoderFFmpegPrivate::open()
qWarning("Failed to create hw frame context for '%s'", codec_name.toLatin1().constData());
return false;
}
// get sw formats
const void *hwcfg = NULL;
AVHWFramesConstraints *constraints = av_hwdevice_get_hwframe_constraints(hw_device_ctx, hwcfg);
const AVPixelFormat* in_fmts = constraints->valid_sw_formats;
if (in_fmts) {
while (*in_fmts != AVPixelFormat(-1)) {
sw_fmts.append(*in_fmts);
++in_fmts;
}
}
av_hwframe_constraints_free(&constraints);
if (format_used == AVPixelFormat(-1))
format_used = VideoFormat::pixelFormatFromFFmpeg(sw_fmts[0]);
// encoder surface pool parameters
AVHWFramesContext* hwfs = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
// encoder surface parameters
hwfs->format = hwfmt;
//hwframes->sw_format = ;// default is use the first constraints.valid_sw_formats

hwfs->format = hwfmt; // must the same as avctx->pix_fmt
hwfs->sw_format = sw_fmts[0]; // if it's not set, vaapi will choose the last valid_sw_formats, but that's wrong for vaGetImage/DeriveImage. nvenc always need sw_format
// hw upload parameters. encoder's hwframes is just for parameter checking, will never be intialized, so we allocate an individual one.
hwframes_ref = av_hwframe_ctx_alloc(hw_device_ctx);
if (!hwframes_ref) {
#ifdef HAVE_AVHWCTX
qWarning("Failed to create hw frame context for uploading '%s'", codec_name.toLatin1().constData());
} else {
hwframes = (AVHWFramesContext*)hwframes_ref->data;
hwframes->format = hwfmt;
const void *hwcfg = NULL;
AVHWFramesConstraints *constraints = av_hwdevice_get_hwframe_constraints(hw_device_ctx, hwcfg);
const AVPixelFormat* in_fmts = constraints->valid_sw_formats;
if (in_fmts) {
while (*in_fmts != AVPixelFormat(-1)) {
sw_fmts.append(*in_fmts);
++in_fmts;
}
}
av_hwframe_constraints_free(&constraints);
}
#endif //HAVE_AVHWCTX
} else {
Expand All @@ -190,20 +196,18 @@ bool VideoEncoderFFmpegPrivate::open()
avctx->time_base = av_d2q(1.0/VideoEncoder::defaultFrameRate(), VideoEncoder::defaultFrameRate()*1001.0+2);
qDebug("size: %dx%d tbc: %f=%d/%d", width, height, av_q2d(avctx->time_base), avctx->time_base.num, avctx->time_base.den);
avctx->bit_rate = bit_rate;
#if 1
//AVDictionary *dict = 0;
if(avctx->codec_id == QTAV_CODEC_ID(H264)) {
avctx->gop_size = 10;
//avctx->max_b_frames = 3;//h264
av_dict_set(&dict, "preset", "fast", 0);
av_dict_set(&dict, "tune", "zerolatency", 0);
av_dict_set(&dict, "profile", "main", 0);
//av_dict_set(&dict, "profile", "main", 0); // conflict with vaapi (int values)
}
if(avctx->codec_id == AV_CODEC_ID_HEVC){
av_dict_set(&dict, "preset", "ultrafast", 0);
av_dict_set(&dict, "tune", "zero-latency", 0);
}
#endif
applyOptionsForContext();
AV_ENSURE_OK(avcodec_open2(avctx, codec, &dict), false);
// from mpv ao_lavc
Expand All @@ -229,6 +233,20 @@ VideoEncoderId VideoEncoderFFmpeg::id() const
return VideoEncoderId_FFmpeg;
}

void VideoEncoderFFmpeg::setHWDevice(const QString &name)
{
DPTR_D(VideoEncoderFFmpeg);
if (d.hwdev == name)
return;
d.hwdev = name;
Q_EMIT hwDeviceChanged();
}

QString VideoEncoderFFmpeg::hwDevice() const
{
return d_func().hwdev;
}

struct ScopedAVFrameDeleter
{
static inline void cleanup(void *pointer) {
Expand Down Expand Up @@ -283,10 +301,10 @@ bool VideoEncoderFFmpeg::encode(const VideoFrame &frame)
// reinit or got an unsupported format. assume parameters will not change, so it's the 1st init
// check constraints
bool init_frames_ctx = d.hwframes->sw_format == AVPixelFormat(-1);
if (d.sw_fmts.contains(pixfmt)) {
pixfmt = d.sw_fmts[0];
if (d.sw_fmts.contains(pixfmt)) { // format changed
init_frames_ctx = true;
} else { // convert to supported sw format
pixfmt = d.sw_fmts[0];
f->format = pixfmt;
VideoFrame converted = frame.to(VideoFormat::pixelFormatFromFFmpeg(pixfmt));
for (int i = 0; i < converted.planeCount(); ++i) {
Expand Down Expand Up @@ -338,3 +356,4 @@ bool VideoEncoderFFmpeg::encode(const VideoFrame &frame)
}

} //namespace QtAV
#include "VideoEncoderFFmpeg.moc"

0 comments on commit a0e58de

Please sign in to comment.