499 lines
17 KiB
C++
499 lines
17 KiB
C++
/******************************************************************************
|
|
QtAV: Multimedia framework based on Qt and FFmpeg
|
|
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
|
|
|
|
* This file is part of QtAV (from 2013)
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
******************************************************************************/
|
|
#include "VideoDecoderFFmpegBase.h"
|
|
#include "AVCompat.h"
|
|
#include "factory.h"
|
|
#include "version.h"
|
|
#include "Logger.h"
|
|
|
|
#define REMOVE_FFMPEG_HW 1
|
|
|
|
/*!
|
|
* options (properties) are from libavcodec/options_table.h
|
|
* enum name here must convert to lower case to fit the names in avcodec. done in AVDecoder.setOptions()
|
|
* Don't use lower case here because the value name may be "default" in avcodec which is a keyword of C++
|
|
*/
|
|
|
|
namespace FAV {
|
|
|
|
class VideoDecoderFFmpegPrivate;
|
|
class VideoDecoderFFmpeg : public VideoDecoderFFmpegBase
|
|
{
|
|
Q_OBJECT
|
|
DPTR_DECLARE_PRIVATE(VideoDecoderFFmpeg)
|
|
Q_PROPERTY(QString codecName READ codecName WRITE setCodecName NOTIFY codecNameChanged)
|
|
#if !(REMOVE_FFMPEG_HW)
|
|
Q_PROPERTY(QString hwaccel READ hwaccel WRITE setHwaccel NOTIFY hwaccelChanged)
|
|
#endif
|
|
Q_PROPERTY(DiscardType skip_loop_filter READ skipLoopFilter WRITE setSkipLoopFilter)
|
|
Q_PROPERTY(DiscardType skip_idct READ skipIDCT WRITE setSkipIDCT)
|
|
// Force a strict standard compliance when encoding (accepted values: -2 to 2)
|
|
//Q_PROPERTY(StrictType strict READ strict WRITE setStrict)
|
|
Q_PROPERTY(DiscardType skip_frame READ skipFrame WRITE setSkipFrame)
|
|
Q_PROPERTY(int threads READ threads WRITE setThreads) // 0 is auto
|
|
Q_PROPERTY(ThreadFlags thread_type READ threadFlags WRITE setThreadFlags)
|
|
Q_PROPERTY(MotionVectorVisFlags vismv READ motionVectorVisFlags WRITE setMotionVectorVisFlags)
|
|
//Q_PROPERTY(BugFlags bug READ bugFlags WRITE setBugFlags)
|
|
Q_ENUMS(StrictType)
|
|
Q_ENUMS(DiscardType)
|
|
Q_ENUMS(ThreadFlag)
|
|
Q_FLAGS(ThreadFlags)
|
|
Q_ENUMS(MotionVectorVisFlag)
|
|
Q_FLAGS(MotionVectorVisFlags)
|
|
Q_ENUMS(BugFlag)
|
|
Q_FLAGS(BugFlags)
|
|
public:
|
|
enum StrictType {
|
|
Very = FF_COMPLIANCE_VERY_STRICT,
|
|
Strict = FF_COMPLIANCE_STRICT,
|
|
Normal = FF_COMPLIANCE_NORMAL, //default
|
|
Unofficial = FF_COMPLIANCE_UNOFFICIAL,
|
|
Experimental = FF_COMPLIANCE_EXPERIMENTAL
|
|
};
|
|
|
|
enum DiscardType { // TODO: discard_type
|
|
None = AVDISCARD_NONE,
|
|
Default = AVDISCARD_DEFAULT, //default
|
|
NoRef = AVDISCARD_NONREF,
|
|
Bidir = AVDISCARD_BIDIR,
|
|
NoKey = AVDISCARD_NONKEY,
|
|
All = AVDISCARD_ALL
|
|
};
|
|
|
|
enum ThreadFlag {
|
|
DefaultType = FF_THREAD_SLICE | FF_THREAD_FRAME,//default
|
|
Slice = FF_THREAD_SLICE,
|
|
Frame = FF_THREAD_FRAME
|
|
};
|
|
Q_DECLARE_FLAGS(ThreadFlags, ThreadFlag)
|
|
// flags. visualize motion vectors (MVs)
|
|
enum MotionVectorVisFlag {
|
|
No = 0, //default
|
|
PF = FF_DEBUG_VIS_MV_P_FOR,
|
|
BF = FF_DEBUG_VIS_MV_B_FOR,
|
|
BB = FF_DEBUG_VIS_MV_B_BACK
|
|
};
|
|
Q_DECLARE_FLAGS(MotionVectorVisFlags, MotionVectorVisFlag)
|
|
enum BugFlag {
|
|
autodetect = FF_BUG_AUTODETECT, //default
|
|
#if FF_API_OLD_MSMPEG4
|
|
//old_msmpeg4 = FF_BUG_OLD_MSMPEG4, //moc does not support PP?
|
|
#endif
|
|
xvid_ilace = FF_BUG_XVID_ILACE,
|
|
ump4 = FF_BUG_UMP4,
|
|
no_padding = FF_BUG_NO_PADDING,
|
|
amv = FF_BUG_AMV,
|
|
#if FF_API_AC_VLC
|
|
//ac_vlc = FF_BUG_AC_VLC, //moc does not support PP?
|
|
#endif
|
|
qpel_chroma = FF_BUG_QPEL_CHROMA,
|
|
std_qpel = FF_BUG_QPEL_CHROMA2,
|
|
direct_blocksize = FF_BUG_DIRECT_BLOCKSIZE,
|
|
edge = FF_BUG_EDGE,
|
|
hpel_chroma = FF_BUG_HPEL_CHROMA,
|
|
dc_clip = FF_BUG_DC_CLIP,
|
|
ms = FF_BUG_MS,
|
|
trunc = FF_BUG_TRUNCATED
|
|
};
|
|
Q_DECLARE_FLAGS(BugFlags, BugFlag)
|
|
|
|
static VideoDecoder* createMMAL() {
|
|
VideoDecoderFFmpeg *vd = new VideoDecoderFFmpeg();
|
|
vd->setProperty("hwaccel", "mmal");
|
|
return vd;
|
|
}
|
|
static VideoDecoder* createQSV() {
|
|
VideoDecoderFFmpeg *vd = new VideoDecoderFFmpeg();
|
|
vd->setProperty("hwaccel", "qsv");
|
|
return vd;
|
|
}
|
|
static VideoDecoder* createCrystalHD() {
|
|
VideoDecoderFFmpeg *vd = new VideoDecoderFFmpeg();
|
|
vd->setProperty("hwaccel", "crystalhd");
|
|
return vd;
|
|
}
|
|
#if !(REMOVE_FFMPEG_HW)
|
|
static void registerHWA() {
|
|
#if defined(Q_OS_WIN32) || (defined(Q_OS_LINUX) && !defined(Q_PROCESSOR_ARM) && !defined(QT_ARCH_ARM))
|
|
VideoDecoder::Register(VideoDecoderId_QSV, createQSV, "QSV");
|
|
#endif
|
|
#ifdef Q_OS_LINUX
|
|
#if defined(Q_PROCESSOR_ARM)/*qt5*/ || defined(QT_ARCH_ARM) /*qt4*/
|
|
VideoDecoder::Register(VideoDecoderId_MMAL, createMMAL, "MMAL");
|
|
#else
|
|
VideoDecoder::Register(VideoDecoderId_CrystalHD, createCrystalHD, "CrystalHD");
|
|
#endif
|
|
#endif
|
|
}
|
|
#endif // REMOVE_FFMPEG_HW
|
|
VideoDecoderFFmpeg();
|
|
VideoDecoderId id() const Q_DECL_OVERRIDE Q_DECL_FINAL;
|
|
QString description() const Q_DECL_OVERRIDE Q_DECL_FINAL
|
|
{
|
|
const int patch = QTAV_VERSION_PATCH(avcodec_version());
|
|
return QStringLiteral("%1 avcodec %2.%3.%4")
|
|
.arg(patch>=100?QStringLiteral("FFmpeg"):QStringLiteral("Libav"))
|
|
.arg(QTAV_VERSION_MAJOR(avcodec_version())).arg(QTAV_VERSION_MINOR(avcodec_version())).arg(patch);
|
|
}
|
|
virtual VideoFrame frame() Q_DECL_OVERRIDE Q_DECL_FINAL;
|
|
|
|
// TODO: av_opt_set in setter
|
|
void setSkipLoopFilter(DiscardType value);
|
|
DiscardType skipLoopFilter() const;
|
|
void setSkipIDCT(DiscardType value);
|
|
DiscardType skipIDCT() const;
|
|
void setStrict(StrictType value);
|
|
StrictType strict() const;
|
|
void setSkipFrame(DiscardType value);
|
|
DiscardType skipFrame() const;
|
|
void setThreads(int value);
|
|
int threads() const;
|
|
void setThreadFlags(ThreadFlags value);
|
|
ThreadFlags threadFlags() const;
|
|
void setMotionVectorVisFlags(MotionVectorVisFlags value);
|
|
MotionVectorVisFlags motionVectorVisFlags() const;
|
|
void setBugFlags(BugFlags value);
|
|
BugFlags bugFlags() const;
|
|
#if !(REMOVE_FFMPEG_HW)
|
|
void setHwaccel(const QString& value);
|
|
QString hwaccel() const;
|
|
#endif
|
|
Q_SIGNALS:
|
|
void codecNameChanged() Q_DECL_OVERRIDE;
|
|
#if !(REMOVE_FFMPEG_HW)
|
|
void hwaccelChanged();
|
|
#endif
|
|
};
|
|
|
|
extern VideoDecoderId VideoDecoderId_FFmpeg;
|
|
FACTORY_REGISTER(VideoDecoder, FFmpeg, "FFmpeg")
|
|
|
|
#if !(REMOVE_FFMPEG_HW)
|
|
void RegisterFFmpegHWA_Man() {
|
|
VideoDecoderFFmpeg::registerHWA();
|
|
}
|
|
|
|
namespace {
|
|
static const struct factory_register_FFmpegHWA {
|
|
inline factory_register_FFmpegHWA() {
|
|
VideoDecoderFFmpeg::registerHWA();
|
|
}
|
|
} sInit_FFmpegHWA;
|
|
}
|
|
#endif // #if !(REMOVE_FFMPEG_HW)
|
|
|
|
class VideoDecoderFFmpegPrivate Q_DECL_FINAL: public VideoDecoderFFmpegBasePrivate
|
|
{
|
|
public:
|
|
VideoDecoderFFmpegPrivate():
|
|
VideoDecoderFFmpegBasePrivate()
|
|
, skip_loop_filter(VideoDecoderFFmpeg::Default)
|
|
, skip_idct(VideoDecoderFFmpeg::Default)
|
|
, strict(VideoDecoderFFmpeg::Normal)
|
|
, skip_frame(VideoDecoderFFmpeg::Default)
|
|
, thread_type(VideoDecoderFFmpeg::DefaultType) //
|
|
, threads(0)
|
|
, debug_mv(VideoDecoderFFmpeg::No)
|
|
, bug(VideoDecoderFFmpeg::autodetect)
|
|
{
|
|
|
|
}
|
|
bool open() Q_DECL_OVERRIDE
|
|
{
|
|
av_opt_set_int(codec_ctx, "skip_loop_filter", (int64_t)skip_loop_filter, 0);
|
|
av_opt_set_int(codec_ctx, "skip_idct", (int64_t)skip_idct, 0);
|
|
av_opt_set_int(codec_ctx, "strict", (int64_t)strict, 0);
|
|
av_opt_set_int(codec_ctx, "skip_frame", (int64_t)skip_frame, 0);
|
|
av_opt_set_int(codec_ctx, "threads", (int64_t)threads, 0);
|
|
av_opt_set_int(codec_ctx, "thread_type", (int64_t)thread_type, 0);
|
|
av_opt_set_int(codec_ctx, "vismv", (int64_t)debug_mv, 0);
|
|
av_opt_set_int(codec_ctx, "bug", (int64_t)bug, 0);
|
|
|
|
//qInfo() << "thread_type:" << thread_type;
|
|
//CODEC_FLAG_EMU_EDGE: deprecated in ffmpeg >=? & libav>=10. always set by ffmpeg
|
|
#if 0
|
|
if (fast) {
|
|
codec_ctx->flags2 |= CODEC_FLAG2_FAST; // TODO:
|
|
} else {
|
|
//codec_ctx->flags2 &= ~CODEC_FLAG2_FAST; //ffplay has no this
|
|
}
|
|
// lavfilter
|
|
//codec_ctx->slice_flags |= SLICE_FLAG_ALLOW_FIELD; //lavfilter
|
|
//codec_ctx->strict_std_compliance = FF_COMPLIANCE_STRICT;
|
|
codec_ctx->thread_safe_callbacks = true;
|
|
switch (codec_ctx->codec_id) {
|
|
case QTAV_CODEC_ID(MPEG4):
|
|
case QTAV_CODEC_ID(H263):
|
|
codec_ctx->thread_type = 0;
|
|
break;
|
|
case QTAV_CODEC_ID(MPEG1VIDEO):
|
|
case QTAV_CODEC_ID(MPEG2VIDEO):
|
|
codec_ctx->thread_type &= ~FF_THREAD_SLICE;
|
|
/* fall through */
|
|
# if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 1, 0))
|
|
case QTAV_CODEC_ID(H264):
|
|
case QTAV_CODEC_ID(VC1):
|
|
case QTAV_CODEC_ID(WMV3):
|
|
codec_ctx->thread_type &= ~FF_THREAD_FRAME;
|
|
# endif
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
int skip_loop_filter;
|
|
int skip_idct;
|
|
int strict;
|
|
int skip_frame;
|
|
int thread_type;
|
|
int threads;
|
|
int debug_mv;
|
|
int bug;
|
|
QString hwa;
|
|
};
|
|
|
|
VideoDecoderFFmpeg::VideoDecoderFFmpeg():
|
|
VideoDecoderFFmpegBase(*new VideoDecoderFFmpegPrivate())
|
|
{
|
|
// dynamic properties about static property details. used by UI
|
|
// format: detail_property
|
|
setProperty("detail_skip_loop_filter", "Skipping the loop filter (aka deblocking) usually has determinal effect on quality. However it provides a big speedup for hi definition streams");
|
|
// like skip_frame
|
|
setProperty("detail_skip_idct", "Force skipping of idct to speed up decoding for frame types (-1=None, "
|
|
"0=Default, 1=B-frames, 2=P-frames, 3=B+P frames, 4=all frames)");
|
|
setProperty("detail_skip_frame","Force skipping frames for speed up decoding.");
|
|
setProperty("detail_threads", QString("%1\n%2\n%3")
|
|
.arg("Number of decoding threads. Set before open. Maybe no effect for some decoders")
|
|
.arg("0: auto")
|
|
.arg("1: single thread decoding"));
|
|
}
|
|
|
|
VideoDecoderId VideoDecoderFFmpeg::id() const
|
|
{
|
|
#if !(REMOVE_FFMPEG_HW)
|
|
DPTR_D(const VideoDecoderFFmpeg);
|
|
if (d.hwa == QLatin1String("mmal"))
|
|
return VideoDecoderId_MMAL;
|
|
if (d.hwa == QLatin1String("qsv"))
|
|
return VideoDecoderId_QSV;
|
|
if (d.hwa == QLatin1String("crystalhd"))
|
|
return VideoDecoderId_CrystalHD;
|
|
#endif
|
|
return VideoDecoderId_FFmpeg;
|
|
}
|
|
#if (1)
|
|
VideoFrame VideoDecoderFFmpeg::frame()
|
|
{
|
|
DPTR_D(VideoDecoderFFmpeg);
|
|
if (d.frame->width <= 0 || d.frame->height <= 0 || !d.codec_ctx)
|
|
return VideoFrame();
|
|
|
|
// it's safe if width, height, pixfmt will not change, only data change
|
|
VideoFrame frame(d.frame->width, d.frame->height, VideoFormat((int)d.codec_ctx->pix_fmt));
|
|
frame.setDisplayAspectRatio(d.getDAR(d.frame));
|
|
frame.setBits(d.frame->data);
|
|
frame.setBytesPerLine(d.frame->linesize);
|
|
// in s. TODO: what about AVFrame.pts? av_frame_get_best_effort_timestamp? move to VideoFrame::from(AVFrame*)
|
|
//frame.setTimestamp((double)d.frame->pkt_pts/1000.0);
|
|
//qInfo() << "### SETPTS:" << d.frame->pkt_pts << d.frame->pts << "DTS:" << d.frame->pkt_dts << __FUNCTION__;
|
|
frame.setPTS((double)d.frame->pkt_pts/1000.0);
|
|
frame.setDuration(((double)d.frame->pkt_duration)/1000.0); // 2024/02/07 추가 KEYFRAME 도 추가가능..
|
|
|
|
frame.setMetaData(QStringLiteral("avbuf"), QVariant::fromValue(AVFrameBuffersRef(new AVFrameBuffers(d.frame))));
|
|
d.updateColorDetails(&frame);
|
|
if (frame.format().hasPalette()) {
|
|
frame.setMetaData(QStringLiteral("pallete"), QByteArray((const char*)d.frame->data[1], 256*4));
|
|
}
|
|
return frame;
|
|
}
|
|
#else
|
|
VideoFrame VideoDecoderFFmpeg::frame()
|
|
{
|
|
DPTR_D(VideoDecoderFFmpeg);
|
|
if (!d.codec_ctx)
|
|
return VideoFrame();
|
|
|
|
if(d.frame->width == 0)
|
|
{
|
|
d.frame->width = 1280;
|
|
d.frame->height = 720;
|
|
d.frame->linesize[0] = 1280;
|
|
d.frame->linesize[1] = 640;
|
|
d.frame->linesize[2] = 640;
|
|
|
|
}
|
|
|
|
//qDebug() << "format" << (int)(d.codec_ctx->pix_fmt) << "width" << d.frame->width << "height" << d.frame->height;
|
|
|
|
|
|
VideoFrame frame(d.frame->width, d.frame->height, VideoFormat((int)0)); // VideoFormat::Format_YUV420P
|
|
|
|
// it's safe if width, height, pixfmt will not change, only data change
|
|
// VideoFrame frame(d.frame->width, d.frame->height, VideoFormat((int)d.codec_ctx->pix_fmt));
|
|
frame.setDisplayAspectRatio(d.getDAR(d.frame));
|
|
frame.setBits(d.frame->data);
|
|
frame.setBytesPerLine(d.frame->linesize);
|
|
// in s. TODO: what about AVFrame.pts? av_frame_get_best_effort_timestamp? move to VideoFrame::from(AVFrame*)
|
|
frame.setTimestamp((double)d.frame->pkt_pts/1000.0);
|
|
frame.setMetaData(QStringLiteral("avbuf"), QVariant::fromValue(AVFrameBuffersRef(new AVFrameBuffers(d.frame))));
|
|
d.updateColorDetails(&frame);
|
|
if (frame.format().hasPalette()) {
|
|
frame.setMetaData(QStringLiteral("pallete"), QByteArray((const char*)d.frame->data[1], 256*4));
|
|
}
|
|
return frame;
|
|
}
|
|
#endif
|
|
void VideoDecoderFFmpeg::setSkipLoopFilter(DiscardType value)
|
|
{
|
|
DPTR_D(VideoDecoderFFmpeg);
|
|
d.skip_loop_filter = value;
|
|
if (d.codec_ctx)
|
|
av_opt_set_int(d.codec_ctx, "skip_loop_filter", (int64_t)value, 0);
|
|
}
|
|
|
|
VideoDecoderFFmpeg::DiscardType VideoDecoderFFmpeg::skipLoopFilter() const
|
|
{
|
|
return (DiscardType)d_func().skip_loop_filter;
|
|
}
|
|
|
|
void VideoDecoderFFmpeg::setSkipIDCT(DiscardType value)
|
|
{
|
|
DPTR_D(VideoDecoderFFmpeg);
|
|
d.skip_idct = (int)value;
|
|
if (d.codec_ctx)
|
|
av_opt_set_int(d.codec_ctx, "skip_idct", (int64_t)value, 0);
|
|
}
|
|
|
|
VideoDecoderFFmpeg::DiscardType VideoDecoderFFmpeg::skipIDCT() const
|
|
{
|
|
return (DiscardType)d_func().skip_idct;
|
|
}
|
|
|
|
void VideoDecoderFFmpeg::setStrict(StrictType value)
|
|
{
|
|
DPTR_D(VideoDecoderFFmpeg);
|
|
d.strict = (int)value;
|
|
if (d.codec_ctx)
|
|
av_opt_set_int(d.codec_ctx, "strict", int64_t(value), 0);
|
|
}
|
|
|
|
VideoDecoderFFmpeg::StrictType VideoDecoderFFmpeg::strict() const
|
|
{
|
|
return (StrictType)d_func().strict;
|
|
}
|
|
|
|
void VideoDecoderFFmpeg::setSkipFrame(DiscardType value)
|
|
{
|
|
DPTR_D(VideoDecoderFFmpeg);
|
|
d.skip_frame = (int)value;
|
|
if (d.codec_ctx)
|
|
av_opt_set_int(d.codec_ctx, "skip_frame", (int64_t)value, 0);
|
|
}
|
|
|
|
VideoDecoderFFmpeg::DiscardType VideoDecoderFFmpeg::skipFrame() const
|
|
{
|
|
return (DiscardType)d_func().skip_frame;
|
|
}
|
|
|
|
void VideoDecoderFFmpeg::setThreads(int value)
|
|
{
|
|
DPTR_D(VideoDecoderFFmpeg);
|
|
d.threads = value;
|
|
if (d.codec_ctx)
|
|
av_opt_set_int(d.codec_ctx, "threads", (int64_t)value, 0);
|
|
}
|
|
|
|
int VideoDecoderFFmpeg::threads() const
|
|
{
|
|
return d_func().threads;
|
|
}
|
|
|
|
void VideoDecoderFFmpeg::setThreadFlags(ThreadFlags value)
|
|
{
|
|
DPTR_D(VideoDecoderFFmpeg);
|
|
d.thread_type = (int)value;
|
|
if (d.codec_ctx)
|
|
av_opt_set_int(d.codec_ctx, "thread_type", (int64_t)value, 0);
|
|
}
|
|
|
|
VideoDecoderFFmpeg::ThreadFlags VideoDecoderFFmpeg::threadFlags() const
|
|
{
|
|
return (ThreadFlags)d_func().thread_type;
|
|
}
|
|
|
|
void VideoDecoderFFmpeg::setMotionVectorVisFlags(MotionVectorVisFlags value)
|
|
{
|
|
DPTR_D(VideoDecoderFFmpeg);
|
|
d.debug_mv = (int)value;
|
|
if (d.codec_ctx)
|
|
av_opt_set_int(d.codec_ctx, "vismv", (int64_t)value, 0);
|
|
}
|
|
|
|
VideoDecoderFFmpeg::MotionVectorVisFlags VideoDecoderFFmpeg::motionVectorVisFlags() const
|
|
{
|
|
return (MotionVectorVisFlags)d_func().debug_mv;
|
|
}
|
|
|
|
void VideoDecoderFFmpeg::setBugFlags(BugFlags value)
|
|
{
|
|
DPTR_D(VideoDecoderFFmpeg);
|
|
d.bug = (int)value;
|
|
if (d.codec_ctx)
|
|
av_opt_set_int(d.codec_ctx, "bug", (int64_t)value, 0);
|
|
}
|
|
|
|
VideoDecoderFFmpeg::BugFlags VideoDecoderFFmpeg::bugFlags() const
|
|
{
|
|
return (BugFlags)d_func().bug;
|
|
}
|
|
#if !(REMOVE_FFMPEG_HW)
|
|
void VideoDecoderFFmpeg::setHwaccel(const QString &value)
|
|
{
|
|
DPTR_D(VideoDecoderFFmpeg);
|
|
if (d.hwa == value)
|
|
return;
|
|
d.hwa = value.toLower();
|
|
Q_EMIT hwaccelChanged();
|
|
}
|
|
|
|
QString VideoDecoderFFmpeg::hwaccel() const
|
|
{
|
|
return d_func().hwa;
|
|
}
|
|
#endif
|
|
void i18n()
|
|
{
|
|
"codecName";
|
|
"skip_loop_filter";
|
|
"skip_idct";
|
|
"strict";
|
|
"skip_frame";
|
|
"threads";
|
|
"thread_type";
|
|
"vismv";
|
|
"bug";
|
|
}
|
|
} //namespace FAV
|
|
|
|
#include "VideoDecoderFFmpeg.moc"
|