533 changes: 533 additions & 0 deletions mythtv/libs/libmythtv/vaapicontext.cpp

Large diffs are not rendered by default.

56 changes: 56 additions & 0 deletions mythtv/libs/libmythtv/vaapicontext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#ifndef VAAPICONTEXT_H
#define VAAPICONTEXT_H

extern "C" {
#include "libavcodec/vaapi.h"
}
#include "va/va_glx.h"

struct vaapi_surface
{
VASurfaceID m_id;
};

class VAAPIDisplay;
class OpenGLVideo;

class VAAPIContext
{
public:
static bool IsFormatAccelerated(QSize size, MythCodecID codec,
PixelFormat &pix_fmt);
VAAPIContext(MythCodecID codec);
~VAAPIContext();

bool CreateDisplay(QSize size);
bool CreateBuffers(void);
void* GetVideoSurface(int i);
uint8_t* GetSurfaceIDPointer(void* buf);

int GetNumBuffers(void) { return m_numSurfaces; }
PixelFormat GetPixelFormat(void) { return m_pix_fmt; }

bool CopySurfaceToTexture(const void* buf, uint texture,
uint texture_type, FrameScanType scan);
void* GetGLXSurface(uint texture, uint texture_type);
void ClearGLXSurfaces(void);

bool InitDisplay(void);
bool InitProfiles(void);
bool InitBuffers(void);
bool InitContext(void);

vaapi_context m_ctx;
MythCodecID m_codec;
QSize m_size;
VAAPIDisplay *m_display;
VAProfile m_vaProfile;
VAEntrypoint m_vaEntrypoint;
PixelFormat m_pix_fmt;
int m_numSurfaces;
VASurfaceID *m_surfaces;
vaapi_surface *m_surfaceData;
QHash<uint, void*> m_glxSurfaces;
};

#endif // VAAPICONTEXT_H
20 changes: 20 additions & 0 deletions mythtv/libs/libmythtv/videodisplayprofile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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["vaapi"] = QObject::tr("VAAPI acceleration");
dec_name["dxva2"] = QObject::tr("Windows hardware acceleration");
}

Expand Down Expand Up @@ -680,6 +681,10 @@ QString VideoDisplayProfile::GetDecoderHelp(QString decoder)
"accelerate video decoding and playback "
"(requires Windows Vista or later).");

if (decoder == "vaapi")
msg += QObject::tr(
"VAAPI will attempt to use the graphics hardware to "
"accelerate video decoding.");
return msg;
}

Expand Down Expand Up @@ -737,6 +742,10 @@ QString VideoDisplayProfile::GetDeinterlacerName(const QString short_name)
return QObject::tr("Advanced (1x, HW)");
else if ("vdpauadvanceddoublerate" == short_name)
return QObject::tr("Advanced (2x, HW)");
else if ("vaapionefield" == short_name)
return QObject::tr("One Field (1x, HW)");
else if ("vaapibobdeint" == short_name)
return QObject::tr("Bob (2x, HW)");

return "";
}
Expand Down Expand Up @@ -1167,6 +1176,13 @@ QString VideoDisplayProfile::GetVideoRendererHelp(const QString &renderer)
"This is the only video renderer for NVidia VDPAU decoding.");
}

if (renderer == "openglvaapi")
{
msg = QObject::tr(
"This video renderer uses VAAPI for video decoding and "
"OpenGL for scaling and color conversion.");
}

return msg;
}

Expand Down Expand Up @@ -1300,6 +1316,10 @@ QString VideoDisplayProfile::GetDeinterlacerHelp(const QString &deint)
msg = kBasicMsg + " " + kDoubleRateMsg + " " + kUsingGPU;
else if (deint == "vdpauadvanceddoublerate")
msg = kAdvMsg + " " + kDoubleRateMsg + " " + kUsingGPU;
else if (deint == "vaapionefield")
msg = kOneFieldMsg + " " + kUsingGPU;
else if (deint == "vaapibobdeint")
msg = kBobMsg + " " + kUsingGPU;
else
msg = QObject::tr("'%1' has not been documented yet.").arg(deint);

Expand Down
4 changes: 3 additions & 1 deletion mythtv/libs/libmythtv/videoout_d3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -700,8 +700,10 @@ MythCodecID VideoOutputD3D::GetBestSupportedCodec(
}


void* VideoOutputD3D::GetDecoderContext(void)
void* VideoOutputD3D::GetDecoderContext(unsigned char* buf, uint8_t*& id)
{
(void)buf;
(void)id;
#ifdef USING_DXVA2
if (m_decoder)
return (void*)&m_decoder->m_context;
Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythtv/videoout_d3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class VideoOutputD3D : public VideoOutput
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;
virtual void* GetDecoderContext(void);
virtual void* GetDecoderContext(unsigned char* buf, uint8_t*& id);

virtual bool CanVisualise(AudioPlayer *audio, MythRender *render)
{ return VideoOutput::CanVisualise(audio, (MythRender*)m_render); }
Expand Down
266 changes: 266 additions & 0 deletions mythtv/libs/libmythtv/videoout_openglvaapi.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
#include "videoout_openglvaapi.h"
#include "vaapicontext.h"

#define LOC QString("VidOutGLVAAPI: ")
#define ERR QString("VidOutGLVAAPI Error: ")

void VideoOutputOpenGLVAAPI::GetRenderOptions(render_opts &opts)
{
opts.renderers->append("openglvaapi");

(*opts.deints)["openglvaapi"].append("vaapionefield");
(*opts.deints)["openglvaapi"].append("vaapibobdeint");
(*opts.deints)["openglvaapi"].append("none");
(*opts.osds)["openglvaapi"].append("opengl2");

if (opts.decoders->contains("vaapi"))
(*opts.safe_renderers)["vaapi"].append("openglvaapi");

if (opts.decoders->contains("ffmpeg"))
(*opts.safe_renderers)["ffmpeg"].append("openglvaapi");

(*opts.safe_renderers)["dummy"].append("openglvaapi");
(*opts.safe_renderers)["nuppel"].append("openglvaapi");

opts.priorities->insert("openglvaapi", 120);
}

VideoOutputOpenGLVAAPI::VideoOutputOpenGLVAAPI()
: VideoOutputOpenGL(), m_ctx(NULL)
{
}

VideoOutputOpenGLVAAPI::~VideoOutputOpenGLVAAPI()
{
TearDown();
}

void VideoOutputOpenGLVAAPI::TearDown(void)
{
DeleteVAAPIContext();
}

bool VideoOutputOpenGLVAAPI::InputChanged(const QSize &input_size, float aspect,
MythCodecID av_codec_id, void *codec_private,
bool &aspect_only)
{
VERBOSE(VB_PLAYBACK, LOC + QString("InputChanged(%1,%2,%3) %4->%5")
.arg(input_size.width()).arg(input_size.height()).arg(aspect)
.arg(toString(video_codec_id)).arg(toString(av_codec_id)));

if (!codec_is_vaapi(av_codec_id))
return VideoOutputOpenGL::InputChanged(input_size, aspect, av_codec_id,
codec_private, aspect_only);

QMutexLocker locker(&gl_context_lock);

bool wasembedding = window.IsEmbedding();
QRect oldrect;
if (wasembedding)
{
oldrect = window.GetEmbeddingRect();
StopEmbedding();
}

bool cid_changed = (video_codec_id != av_codec_id);
bool res_changed = input_size != window.GetActualVideoDim();
bool asp_changed = aspect != window.GetVideoAspect();

if (!res_changed && !cid_changed)
{
if (asp_changed)
{
aspect_only = true;
VideoAspectRatioChanged(aspect);
MoveResize();
if (wasembedding)
EmbedInWidget(oldrect);
}
return true;
}

if (gCoreContext->IsUIThread())
TearDown();
else
DestroyCPUResources();

QRect disp = window.GetDisplayVisibleRect();
if (Init(input_size.width(), input_size.height(),
aspect, gl_parent_win, disp, av_codec_id))
{
if (wasembedding)
EmbedInWidget(oldrect);
if (gCoreContext->IsUIThread())
BestDeint();
return true;
}

VERBOSE(VB_IMPORTANT, ERR +
QString("Failed to re-initialise video output."));
errorState = kError_Unknown;

return false;
}

bool VideoOutputOpenGLVAAPI::Init(int width, int height, float aspect,
WId winid, const QRect &win_rect,
MythCodecID codec_id)
{
bool ok = VideoOutputOpenGL::Init(width, height, aspect, winid,
win_rect, codec_id);
if (ok && codec_is_vaapi(video_codec_id))
return CreateVAAPIContext(window.GetActualVideoDim());
return ok;
}

bool VideoOutputOpenGLVAAPI::CreateVAAPIContext(QSize size)
{
// FIXME During a video stream change this is called from the decoder
// thread - which breaks all other efforts to remove non-UI thread
// access to the OpenGL context. There is no obvious fix however - if we
// don't delete and re-create the VAAPI decoder context immediately then
// the decoder fails and playback exits.
OpenGLLocker ctx_lock(gl_context);

if (m_ctx)
DeleteVAAPIContext();

m_ctx = new VAAPIContext(video_codec_id);
if (m_ctx && m_ctx->CreateDisplay(size) && m_ctx->CreateBuffers())
{
int num_buffers = m_ctx->GetNumBuffers();
const QSize video_dim = window.GetActualVideoDim();

bool ok = true;
for (int i = 0; i < num_buffers; i++)
{
ok &= vbuffers.CreateBuffer(video_dim.width(),
video_dim.height(), i,
m_ctx->GetVideoSurface(i),
FMT_VAAPI);
}
return ok;
}

VERBOSE(VB_IMPORTANT, ERR + QString("Failed to create VAAPI context."));
errorState = kError_Unknown;
return false;
}

void VideoOutputOpenGLVAAPI::DeleteVAAPIContext(void)
{
QMutexLocker locker(&gl_context_lock);
delete m_ctx;
m_ctx = NULL;
}

bool VideoOutputOpenGLVAAPI::CreateBuffers(void)
{
QMutexLocker locker(&gl_context_lock);
if (codec_is_vaapi(video_codec_id))
{
vbuffers.Init(24, true, 2, 1, 4, 1);
return true;
}
return VideoOutputOpenGL::CreateBuffers();
}

void* VideoOutputOpenGLVAAPI::GetDecoderContext(unsigned char* buf, uint8_t*& id)
{
if (m_ctx)
{
id = GetSurfaceIDPointer(buf);
return &m_ctx->m_ctx;
}
return NULL;
}

uint8_t* VideoOutputOpenGLVAAPI::GetSurfaceIDPointer(void* buf)
{
if (m_ctx)
return m_ctx->GetSurfaceIDPointer(buf);
return NULL;
}

void VideoOutputOpenGLVAAPI::SetProfile(void)
{
if (db_vdisp_profile)
db_vdisp_profile->SetVideoRenderer("openglvaapi");
}

bool VideoOutputOpenGLVAAPI::ApproveDeintFilter(const QString &filtername) const
{
return filtername.contains("vaapi");
}

bool VideoOutputOpenGLVAAPI::SetDeinterlacingEnabled(bool enable)
{
m_deinterlacing = enable;
SetupDeinterlace(enable);
return m_deinterlacing;
}

bool VideoOutputOpenGLVAAPI::SetupDeinterlace(bool i, const QString& ovrf)
{
//m_deintfiltername = !db_vdisp_profile ? "" :
// db_vdisp_profile->GetFilteredDeint(ovrf);
m_deinterlacing = i;
return m_deinterlacing;
}

void VideoOutputOpenGLVAAPI::ProcessFrame(VideoFrame *frame, OSD *osd,
FilterChain *filterList,
const PIPMap &pipPlayers,
FrameScanType scan)
{
QMutexLocker locker(&gl_context_lock);
VideoOutputOpenGL::ProcessFrame(frame, osd, filterList, pipPlayers, scan);

if (codec_is_vaapi(video_codec_id) && m_ctx && gl_videochain && frame)
{
gl_context->makeCurrent();
m_ctx->CopySurfaceToTexture(frame->buf,
gl_videochain->GetInputTexture(),
gl_videochain->GetTextureType(), scan);
gl_videochain->SetInputUpdated();
gl_context->doneCurrent();
}
}

QStringList VideoOutputOpenGLVAAPI::GetAllowedRenderers(
MythCodecID myth_codec_id, const QSize &video_dim)
{
(void) video_dim;
QStringList list;
if ((codec_is_std(myth_codec_id) || (codec_is_vaapi(myth_codec_id))) &&
!getenv("NO_VAAPI"))
{
list += "openglvaapi";
}
return list;
}

MythCodecID VideoOutputOpenGLVAAPI::GetBestSupportedCodec(
uint width, uint height,
uint stream_type, bool no_acceleration,
PixelFormat &pix_fmt)
{
QSize size(width, height);
bool use_cpu = no_acceleration;
VideoDisplayProfile vdp;
vdp.SetInput(size);
QString dec = vdp.GetDecoder();

PixelFormat fmt = PIX_FMT_YUV420P;
MythCodecID test_cid = (MythCodecID)(kCodec_MPEG1_VAAPI + (stream_type - 1));
if (codec_is_vaapi(test_cid))
use_cpu |= !VAAPIContext::IsFormatAccelerated(size, test_cid, fmt);
else
use_cpu = true;

if ((dec != "vaapi") || getenv("NO_VAAPI") || use_cpu)
return (MythCodecID)(kCodec_MPEG1 + (stream_type - 1));

pix_fmt = fmt;
return test_cid;
}
48 changes: 48 additions & 0 deletions mythtv/libs/libmythtv/videoout_openglvaapi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#ifndef VIDEOOUTPUTOPENGLVAAPI_H
#define VIDEOOUTPUTOPENGLVAAPI_H

#include "videoout_opengl.h"

class VAAPIContext;

class VideoOutputOpenGLVAAPI : public VideoOutputOpenGL
{
public:
static void GetRenderOptions(render_opts &opts);

VideoOutputOpenGLVAAPI();
~VideoOutputOpenGLVAAPI();

bool Init(int width, int height, float aspect, WId winid,
const QRect &win_rect, MythCodecID codec_id);
bool CreateVAAPIContext(QSize size);
void DeleteVAAPIContext(void);
bool CreateBuffers(void);
virtual void* GetDecoderContext(unsigned char* buf, uint8_t*& id);
uint8_t* GetSurfaceIDPointer(void* buf);
void SetProfile(void);
void TearDown(void);
bool InputChanged(const QSize &input_size, float aspect,
MythCodecID av_codec_id, void *codec_private,
bool &aspect_only);
void ProcessFrame(VideoFrame *frame, OSD *osd,
FilterChain *filterList,
const PIPMap &pipPlayers,
FrameScanType scan);
bool ApproveDeintFilter(const QString& filtername) const;
bool SetDeinterlacingEnabled(bool enable);
bool SetupDeinterlace(bool i, const QString& ovrf="");
void InitPictureAttributes(void) { }

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);

private:
VAAPIContext *m_ctx;
};
#endif // VIDEOOUTPUTOPENGLVAAPI_H

17 changes: 17 additions & 0 deletions mythtv/libs/libmythtv/videooutbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
#include "videoout_vdpau.h"
#endif

#ifdef USING_VAAPI
#include "videoout_openglvaapi.h"
#endif

#include "videoout_null.h"
#include "dithertable.h"

Expand Down Expand Up @@ -88,6 +92,10 @@ void VideoOutput::GetRenderOptions(render_opts &opts)
#ifdef USING_VDPAU
VideoOutputVDPAU::GetRenderOptions(opts);
#endif // USING_VDPAU

#ifdef USING_VAAPI
VideoOutputOpenGLVAAPI::GetRenderOptions(opts);
#endif // USING_VAAPI
}

/**
Expand Down Expand Up @@ -128,6 +136,10 @@ VideoOutput *VideoOutput::Create(
renderers += VideoOutputVDPAU::GetAllowedRenderers(codec_id, video_dim);
#endif // USING_VDPAU

#ifdef USING_VAAPI
renderers += VideoOutputOpenGLVAAPI::GetAllowedRenderers(codec_id, video_dim);
#endif // USING_VAAPI

VERBOSE(VB_PLAYBACK, LOC + "Allowed renderers: " +
to_comma_list(renderers));

Expand Down Expand Up @@ -185,6 +197,11 @@ VideoOutput *VideoOutput::Create(
vo = new VideoOutputVDPAU();
#endif // USING_VDPAU

#ifdef USING_VAAPI
if (renderer == "openglvaapi")
vo = new VideoOutputOpenGLVAAPI();
#endif // USING_VAAPI

#ifdef USING_XV
if (xvlist.contains(renderer))
vo = new VideoOutputXv();
Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythtv/videooutbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ class VideoOutput

/// \brief Return true if HW Acceleration is running
virtual bool hasHWAcceleration(void) const { return false; }
virtual void* GetDecoderContext(void) { return NULL; }
virtual void* GetDecoderContext(unsigned char* buf, uint8_t*& id) { return NULL; }

/// \brief Sets the number of frames played
virtual void SetFramesPlayed(long long fp) { framesPlayed = fp; };
Expand Down