Browse files

Windows: Add DXVA2 hardware decoding support.

- You will need to download the dxva2api.h from the VLC website, install it in an appropriate include path and re-run configure with --enable-dxva2
- Enable Windows Hardware Acceleration decoder support in the display profile of your choice.
- This is alpha quality at best and this version has only been tested on Vista with an NVidia 9400M chipset (MacBook in bootcamp).
- There is no deinterlacing or other video processing support at this time.
- It is largely stable with MPEG2, H.264 and VC1 support all working though there will be fixes required for certain chipsets and codecs (just as for VDPAU).
- Most stability issues appear to be unrelated to DXVA2 and are general issues with this windows build.

- There is significant picture breakup when starting playback and occasional glitches at other times.

If you are going to test (especially if using the new windows build script), please compile a debug build so we can iron out all of the outstanding issues as quickly as possible.
  • Loading branch information...
1 parent e26b681 commit 69fefe53e960f3fd8d0fcde144a44ed4e8301a05 mark-kendall committed Mar 4, 2011
View
64 mythtv/libs/libmythtv/avformatdecoder.cpp
@@ -48,6 +48,10 @@ extern "C" {
}
#endif // USING_VDPAU
+#ifdef USING_DXVA2
+#include "videoout_d3d.h"
+#endif
+
extern "C" {
#include "libavutil/avutil.h"
#include "libavcodec/ac3_parser.h"
@@ -103,6 +107,7 @@ int get_avf_buffer_vdpau(struct AVCodecContext *c, AVFrame *pic);
void release_avf_buffer_vdpau(struct AVCodecContext *c, AVFrame *pic);
void render_slice_vdpau(struct AVCodecContext *s, const AVFrame *src,
int offset[4], int y, int type, int height);
+int get_avf_buffer_dxva2(struct AVCodecContext *c, AVFrame *pic);
static AVCodec *find_vdpau_decoder(AVCodec *c, enum CodecID id)
{
@@ -208,6 +213,10 @@ void AvFormatDecoder::GetDecoders(render_opts &opts)
opts.decoders->append("vdpau");
(*opts.equiv_decoders)["vdpau"].append("dummy");
#endif
+#ifdef USING_DXVA2
+ opts.decoders->append("dxva2");
+ (*opts.equiv_decoders)["dxva2"].append("dummy");
+#endif
PrivateDecoder::GetDecoders(opts);
}
@@ -1249,6 +1258,12 @@ void AvFormatDecoder::InitVideoCodec(AVStream *stream, AVCodecContext *enc,
enc->draw_horiz_band = render_slice_vdpau;
enc->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD;
}
+ else if (CODEC_IS_DXVA2(codec, enc))
+ {
+ enc->get_buffer = get_avf_buffer_dxva2;
+ enc->get_format = get_format_dxva2;
+ enc->release_buffer = release_avf_buffer;
+ }
else if (codec && codec->capabilities & CODEC_CAP_DR1)
{
enc->flags |= CODEC_FLAG_EMU_EDGE;
@@ -1778,6 +1793,25 @@ int AvFormatDecoder::ScanStreams(bool novideo)
handled = true;
}
#endif // USING_VDPAU
+#ifdef USING_DXVA2
+ MythCodecID dxva2_mcid;
+ PixelFormat pix_fmt = PIX_FMT_YUV420P;
+ dxva2_mcid = VideoOutputD3D::GetBestSupportedCodec(
+ width, height, mpeg_version(enc->codec_id),
+ no_hardware_decoders, pix_fmt);
+
+ if (dxva2_mcid >= video_codec_id)
+ {
+ enc->codec_id = (CodecID)myth2av_codecid(dxva2_mcid);
+ video_codec_id = dxva2_mcid;
+ handled = true;
+ if (!no_hardware_decoders &&
+ codec_is_dxva2(video_codec_id))
+ {
+ enc->pix_fmt = pix_fmt;
+ }
+ }
+#endif // USING_DXVA2
}
if (!handled)
@@ -2300,6 +2334,36 @@ void render_slice_vdpau(struct AVCodecContext *s, const AVFrame *src,
}
}
+int get_avf_buffer_dxva2(struct AVCodecContext *c, AVFrame *pic)
+{
+ AvFormatDecoder *nd = (AvFormatDecoder *)(c->opaque);
+ VideoFrame *frame = nd->GetPlayer()->GetNextVideoFrame();
+ for (int i = 0; i < 4; i++)
+ {
+ pic->data[i] = NULL;
+ pic->linesize[i] = 0;
+ }
+ pic->reordered_opaque = c->reordered_opaque;
+ pic->opaque = frame;
+ pic->type = FF_BUFFER_TYPE_USER;
+ pic->age = 256 * 256 * 256 *64;
+ frame->pix_fmt = c->pix_fmt;
+
+#ifdef USING_DXVA2
+ if (nd->GetPlayer()->getVideoOutput())
+ {
+ VideoOutputD3D *vo =
+ dynamic_cast<VideoOutputD3D*>(nd->GetPlayer()->getVideoOutput());
+ if (vo)
+ c->hwaccel_context = (dxva_context*)vo->GetDXVA2Decoder();
+ pic->data[0] = (uint8_t*)frame->buf;
+ pic->data[3] = (uint8_t*)frame->buf;
+ }
+#endif
+
+ return 0;
+}
+
void AvFormatDecoder::DecodeDTVCC(const uint8_t *buf, uint len)
{
if (!len)
View
4 mythtv/libs/libmythtv/libmythtv.pro
@@ -581,6 +581,10 @@ mingw {
SOURCES -= NuppelVideoRecorder.cpp
SOURCES += videoout_d3d.cpp
+ using_dxva2: DEFINES += USING_DXVA2
+ using_dxva2: HEADERS += dxva2decoder.h
+ using_dxva2: SOURCES += dxva2decoder.cpp
+
LIBS += -lws2_32
}
View
7 mythtv/libs/libmythtv/videodisplayprofile.cpp
@@ -639,6 +639,7 @@ QString VideoDisplayProfile::GetDecoderName(const QString &decoder)
dec_name["ffmpeg"] = QObject::tr("Standard");
dec_name["macaccel"] = QObject::tr("Mac hardware acceleration");
dec_name["vdpau"] = QObject::tr("NVidia VDPAU acceleration");
+ dec_name["dxva2"] = QObject::tr("Windows hardware acceleration");
}
QString ret = decoder;
@@ -673,6 +674,12 @@ QString VideoDisplayProfile::GetDecoderHelp(QString decoder)
"VDPAU will attempt to use the graphics hardware to "
"accelerate video decoding and playback.");
+ if (decoder == "dxva2")
+ msg += QObject::tr(
+ "DXVA2 will use the graphics hardware to "
+ "accelerate video decoding and playback "
+ "(requires Windows Vista or later).");
+
return msg;
}
View
227 mythtv/libs/libmythtv/videoout_d3d.cpp
@@ -30,6 +30,8 @@ const int kPrebufferFramesNormal = 10;
const int kPrebufferFramesSmall = 4;
const int kKeepPrebuffer = 2;
+#define NUM_DXVA2_BUFS 30
+
#define LOC QString("VideoOutputD3D: ")
#define LOC_WARN QString("VideoOutputD3D Warning: ")
#define LOC_ERR QString("VideoOutputD3D Error: ")
@@ -39,15 +41,19 @@ void VideoOutputD3D::GetRenderOptions(render_opts &opts,
{
opts.renderers->append("direct3d");
opts.deints->insert("direct3d", cpudeints);
- (*opts.osds)["direct3d"].append("softblend");
(*opts.osds)["direct3d"].append("direct3d");
(*opts.safe_renderers)["dummy"].append("direct3d");
(*opts.safe_renderers)["nuppel"].append("direct3d");
if (opts.decoders->contains("ffmpeg"))
(*opts.safe_renderers)["ffmpeg"].append("direct3d");
if (opts.decoders->contains("crystalhd"))
(*opts.safe_renderers)["crystalhd"].append("direct3d");
- opts.priorities->insert("direct3d", 55);
+ opts.priorities->insert("direct3d", 70);
+
+#ifdef USING_DXVA2
+ if (opts.decoders->contains("dxva2"))
+ (*opts.safe_renderers)["dxva2"].append("direct3d");
+#endif
}
VideoOutputD3D::VideoOutputD3D(void)
@@ -58,6 +64,10 @@ VideoOutputD3D::VideoOutputD3D(void)
m_osd_painter(NULL)
{
m_pauseFrame.buf = NULL;
+#ifdef USING_DXVA2
+ m_decoder = NULL;
+#endif
+ m_pause_surface = NULL;
}
VideoOutputD3D::~VideoOutputD3D()
@@ -80,6 +90,7 @@ void VideoOutputD3D::TearDown(void)
delete m_osd_painter;
m_osd_painter = NULL;
+ DeleteDecoder();
DestroyContext();
}
@@ -128,25 +139,27 @@ bool VideoOutputD3D::InputChanged(const QSize &input_size,
void *codec_private,
bool &aspect_only)
{
- VERBOSE(VB_PLAYBACK, LOC + QString("InputChanged(%1,%2,%3) %4")
- .arg(input_size.width()).arg(input_size.height()).arg(aspect)
- .arg(toString(av_codec_id)));
-
QMutexLocker locker(&m_lock);
- if (!codec_is_std(av_codec_id))
- {
- VERBOSE(VB_IMPORTANT, LOC_ERR +
- QString("New video codec is not supported."));
- errorState = kError_Unknown;
- return false;
- }
+ QSize cursize = window.GetActualVideoDim();
+
+ VERBOSE(VB_PLAYBACK, LOC +
+ QString("InputChanged from %1: %2x%3 aspect %4 to %5: %6x%7 aspect %9")
+ .arg(toString(video_codec_id)).arg(cursize.width())
+ .arg(cursize.height()).arg(window.GetVideoAspect())
+ .arg(toString(av_codec_id)).arg(input_size.width())
+ .arg(input_size.height()).arg(aspect));
+
- if (input_size == window.GetActualVideoDim())
+ bool cid_changed = (video_codec_id != av_codec_id);
+ bool res_changed = input_size != cursize;
+ bool asp_changed = aspect != window.GetVideoAspect();
+
+ if (!res_changed && !cid_changed)
{
- aspect_only = video_codec_id == av_codec_id;
- if (window.GetVideoAspect() != aspect)
+ if (asp_changed)
{
+ aspect_only = true;
VideoAspectRatioChanged(aspect);
MoveResize();
}
@@ -159,6 +172,7 @@ bool VideoOutputD3D::InputChanged(const QSize &input_size,
aspect, m_hWnd, disp.left(), disp.top(),
disp.width(), disp.height(), av_codec_id, m_hEmbedWnd))
{
+ BestDeint();
return true;
}
@@ -205,12 +219,20 @@ bool VideoOutputD3D::Init(int width, int height, float aspect,
VideoOutput::Init(width, height, aspect, winid,
winx, winy, winw, winh, codec_id, embedid);
+ VERBOSE(VB_PLAYBACK, LOC + QString("Init with codec: %1")
+ .arg(toString(codec_id)));
SetProfile();
bool success = true;
success &= SetupContext();
InitDisplayMeasurements(width, height, false);
+ if (codec_is_dxva2(video_codec_id))
+ {
+ if (!CreateDecoder())
+ return false;
+ }
+
success &= CreateBuffers();
success &= InitBuffers();
success &= CreatePauseFrame();
@@ -241,6 +263,14 @@ void VideoOutputD3D::SetProfile(void)
bool VideoOutputD3D::CreateBuffers(void)
{
+ if (codec_is_dxva2(video_codec_id))
+ {
+ vbuffers.Init(NUM_DXVA2_BUFS, false, 2, 1, 4, 1);
+ VERBOSE(VB_PLAYBACK, LOC + QString("Created %1 empty DXVA2 buffers.")
+ .arg(NUM_DXVA2_BUFS));
+ return true;
+ }
+
vbuffers.Init(kNumBuffers, true, kNeedFreeFrames,
kPrebufferFramesNormal, kPrebufferFramesSmall,
kKeepPrebuffer);
@@ -250,13 +280,33 @@ bool VideoOutputD3D::CreateBuffers(void)
bool VideoOutputD3D::InitBuffers(void)
{
+#ifdef USING_DXVA2
+ if ((codec_is_dxva2(video_codec_id)) && m_decoder)
+ {
+ QMutexLocker locker(&m_lock);
+ const QSize video_dim = window.GetVideoDim();
+ bool ok = true;
+ for (int i = 0; i < NUM_DXVA2_BUFS; i++)
+ {
+ ok &= vbuffers.CreateBuffer(video_dim.width(),
+ video_dim.height(), i,
+ m_decoder->GetSurface(i), FMT_DXVA2);
+ }
+ if (ok)
+ VERBOSE(VB_PLAYBACK, LOC + "Initialised DXVA2 buffers.");
+ return ok;
+ }
+#endif
return vbuffers.CreateBuffers(FMT_YV12,
window.GetVideoDim().width(),
window.GetVideoDim().height());
}
bool VideoOutputD3D::CreatePauseFrame(void)
{
+ if (codec_is_dxva2(video_codec_id))
+ return true;
+
m_pauseFrame.height = vbuffers.GetScratchFrame()->height;
m_pauseFrame.width = vbuffers.GetScratchFrame()->width;
m_pauseFrame.bpp = vbuffers.GetScratchFrame()->bpp;
@@ -277,10 +327,11 @@ void VideoOutputD3D::PrepareFrame(VideoFrame *buffer, FrameScanType t,
return;
}
- if (!buffer)
+ if (!buffer && codec_is_std(video_codec_id))
buffer = vbuffers.GetScratchFrame();
- framesPlayed = buffer->frameNumber + 1;
+ if (buffer)
+ framesPlayed = buffer->frameNumber + 1;
if (!m_render || !m_video)
return;
@@ -373,14 +424,24 @@ void VideoOutputD3D::UpdatePauseFrame(void)
{
QMutexLocker locker(&m_lock);
VideoFrame *used_frame = vbuffers.head(kVideoBuffer_used);
- if (!used_frame)
- used_frame = vbuffers.GetScratchFrame();
- CopyFrame(&m_pauseFrame, used_frame);
+ if (codec_is_std(video_codec_id))
+ {
+ if (!used_frame)
+ used_frame = vbuffers.GetScratchFrame();
+ CopyFrame(&m_pauseFrame, used_frame);
+ }
+ else if (codec_is_dxva2(video_codec_id))
+ {
+ m_pause_surface = used_frame->buf;
+ }
}
void VideoOutputD3D::UpdateFrame(VideoFrame *frame, D3D9Image *img)
{
+ if (codec_is_dxva2(video_codec_id))
+ return;
+
// TODO - add a size check
bool hardware_conv = false;
uint pitch = 0;
@@ -437,6 +498,9 @@ void VideoOutputD3D::ProcessFrame(VideoFrame *frame, OSD *osd,
const PIPMap &pipPlayers,
FrameScanType scan)
{
+ if (!m_render || !m_video)
+ return;
+
QMutexLocker locker(&m_lock);
if (IsErrored())
{
@@ -445,21 +509,31 @@ void VideoOutputD3D::ProcessFrame(VideoFrame *frame, OSD *osd,
return;
}
- bool sw_frame = (frame && (frame->codec == FMT_YV12));
+ bool gpu = codec_is_dxva2(video_codec_id);
+
+ if (gpu && frame && frame->codec != FMT_DXVA2)
+ {
+ VERBOSE(VB_IMPORTANT, LOC_ERR + "Wrong frame format");
+ return;
+ }
+
bool deint_proc = m_deinterlacing && (m_deintFilter != NULL);
bool pauseframe = false;
if (!frame)
{
- frame = vbuffers.GetScratchFrame();
- CopyFrame(vbuffers.GetScratchFrame(), &m_pauseFrame);
+ if (!gpu)
+ {
+ frame = vbuffers.GetScratchFrame();
+ CopyFrame(vbuffers.GetScratchFrame(), &m_pauseFrame);
+ }
pauseframe = true;
}
- if (filterList && sw_frame)
+ if (filterList && !gpu)
filterList->ProcessFrame(frame);
- bool safepauseframe = pauseframe && !IsBobDeint() && sw_frame;
+ bool safepauseframe = pauseframe && !IsBobDeint() && !gpu;
if (deint_proc && m_deinterlaceBeforeOSD &&
(!pauseframe || safepauseframe))
{
@@ -475,13 +549,30 @@ void VideoOutputD3D::ProcessFrame(VideoFrame *frame, OSD *osd,
m_deintFilter->ProcessFrame(frame, scan);
}
- if (m_render)
+ // Test the device
+ m_render_valid |= m_render->Test(m_render_reset);
+ if (m_render_reset)
+ SetupContext();
+
+ // Update a software decoded frame
+ if (m_render_valid && !gpu)
+ UpdateFrame(frame, m_video);
+
+ // Update a GPU decoded frame
+ if (m_render_valid && gpu)
{
- m_render_valid |= m_render->Test(m_render_reset);
+ m_render_valid = m_render->Test(m_render_reset);
if (m_render_reset)
- SetupContext();
- if (m_render_valid && m_video && sw_frame)
- UpdateFrame(frame, m_video);
+ CreateDecoder();
+
+ if (m_render_valid && frame)
+ {
+ m_render->CopyFrame(frame->buf, m_video);
+ }
+ else if (m_render_valid && pauseframe)
+ {
+ m_render->CopyFrame(m_pause_surface, m_video);
+ }
}
}
@@ -567,10 +658,78 @@ QStringList VideoOutputD3D::GetAllowedRenderers(
MythCodecID myth_codec_id, const QSize &video_dim)
{
QStringList list;
+ if (codec_is_std(myth_codec_id) || (codec_is_dxva2_hw(myth_codec_id) &&
+ !getenv("NO_DXVA2")))
+ {
+ list += "direct3d";
+ }
+ return list;
+}
- if (codec_is_std(myth_codec_id) && !getenv("NO_DIRECT3D"))
- list += "direct3d";
+bool VideoOutputD3D::ApproveDeintFilter(const QString& filtername) const
+{
+ if (codec_is_std(video_codec_id))
+ {
+ return !filtername.contains("bobdeint") &&
+ !filtername.contains("opengl") &&
+ !filtername.contains("vdpau");
+ }
- return list;
+ return false;
+}
+
+MythCodecID VideoOutputD3D::GetBestSupportedCodec(
+ uint width, uint height,
+ uint stream_type, bool no_acceleration,
+ PixelFormat &pix_fmt)
+{
+#ifdef USING_DXVA2
+ QSize size(width, height);
+ bool use_cpu = no_acceleration;
+ VideoDisplayProfile vdp;
+ vdp.SetInput(size);
+ QString dec = vdp.GetDecoder();
+
+ MythCodecID test_cid = (MythCodecID)(kCodec_MPEG1_DXVA2 + (stream_type - 1));
+ use_cpu |= !codec_is_dxva2_hw(test_cid);
+ pix_fmt = PIX_FMT_DXVA2_VLD;
+ if ((dec == "dxva2") && !getenv("NO_DXVA2") && !use_cpu)
+ return test_cid;
+#endif
+ return (MythCodecID)(kCodec_MPEG1 + (stream_type - 1));
+}
+
+
+void* VideoOutputD3D::GetDXVA2Decoder(void)
+{
+#ifdef USING_DXVA2
+ if (m_decoder)
+ return (void*)&m_decoder->m_context;
+#endif
+ return NULL;
+}
+
+bool VideoOutputD3D::CreateDecoder(void)
+{
+#ifdef USING_DXVA2
+ QMutexLocker locker(&m_lock);
+ if (m_decoder)
+ DeleteDecoder();
+ QSize video_dim = window.GetVideoDim();
+ m_decoder = new DXVA2Decoder(NUM_DXVA2_BUFS, video_codec_id,
+ video_dim.width(), video_dim.height());
+ return (m_decoder && m_decoder->Init(m_render));
+#endif
+ return false;
}
+
+void VideoOutputD3D::DeleteDecoder(void)
+{
+#ifdef USING_DXVA2
+ QMutexLocker locker(&m_lock);
+ delete m_decoder;
+ m_decoder = NULL;
+#endif
+}
+
/* vim: set expandtab tabstop=4 shiftwidth=4: */
View
18 mythtv/libs/libmythtv/videoout_d3d.h
@@ -8,6 +8,10 @@
#include "mythrender_d3d9.h"
#include "mythpainter_d3d9.h"
+#ifdef USING_DXVA2
+#include "dxva2decoder.h"
+#endif
+
class VideoOutputD3D : public VideoOutput
{
public:
@@ -39,13 +43,20 @@ class VideoOutputD3D : public VideoOutput
bool hasFullScreenOSD(void) const { return true; }
static QStringList GetAllowedRenderers(MythCodecID myth_codec_id,
const QSize &video_dim);
+ static MythCodecID GetBestSupportedCodec(uint width, uint height,
+ uint stream_type,
+ bool no_acceleration,
+ PixelFormat &pix_fmt);
+
void ShowPIP(VideoFrame *frame,
MythPlayer *pipplayer,
PIPLocation loc);
void RemovePIP(MythPlayer *pipplayer);
bool IsPIPSupported(void) const { return false; /*true*/}
virtual MythPainter *GetOSDPainter(void) { return (MythPainter*)m_osd_painter; }
bool hasHWAcceleration(void) const { return !codec_is_std(video_codec_id); }
+ virtual bool ApproveDeintFilter(const QString& filtername) const;
+ void* GetDXVA2Decoder(void);
private:
void TearDown(void);
@@ -72,6 +83,13 @@ class VideoOutputD3D : public VideoOutput
D3D9Image *m_pip_active;
MythD3D9Painter *m_osd_painter;
+
+ bool CreateDecoder(void);
+ void DeleteDecoder(void);
+#ifdef USING_DXVA2
+ DXVA2Decoder *m_decoder;
+#endif
+ void *m_pause_surface;
};
#endif

0 comments on commit 69fefe5

Please sign in to comment.