Skip to content

Commit

Permalink
VideoOutputOpenGL: Fix pause frame for hardware frames
Browse files Browse the repository at this point in the history
- retain the most recent frame for pause as we need a valid video frame
to access the interop class
- software frames are untouched
- optimise rendering VAAPI GLX pause frames by avoiding unnecessary
updates when we already have the current frame in texture memory.
  • Loading branch information
mark-kendall committed Mar 7, 2019
1 parent 853440d commit d00954f
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 18 deletions.
17 changes: 11 additions & 6 deletions mythtv/libs/libmythtv/mythmediacodecinterop.cpp
Expand Up @@ -10,7 +10,6 @@ extern "C" {
}

#define LOC QString("MediaCodecInterop: ")
#define DUMMY_TEXTURE_ID 1

MythMediaCodecInterop* MythMediaCodecInterop::Create(MythRenderOpenGL *Context, QSize Size)
{
Expand Down Expand Up @@ -95,7 +94,7 @@ bool MythMediaCodecInterop::Initialise(QSize Size)
{
vector<MythGLTexture*> result;
result.push_back(texture);
m_openglTextures.insert(DUMMY_TEXTURE_ID, result);
m_openglTextures.insert(DUMMY_INTEROP_ID, result);
LOG(VB_GENERAL, LOG_INFO, LOC + "Created Android Surface");
return true;
}
Expand All @@ -113,10 +112,16 @@ vector<MythGLTexture*> MythMediaCodecInterop::Acquire(MythRenderOpenGL *Context,
FrameScanType)
{
vector<MythGLTexture*> result;
if (!Frame)
return result;

// Pause frame handling
if (!Frame && !m_openglTextures.isEmpty())
return m_openglTextures[DUMMY_TEXTURE_ID];
// Pause frame handling - we can never release the same buffer twice
if (!Frame->buf)
{
if (!m_openglTextures.isEmpty())
return m_openglTextures[DUMMY_INTEROP_ID];
return result;
}

// Sanitise
if (!Context || !ColourSpace || !Frame || m_openglTextures.isEmpty())
Expand Down Expand Up @@ -156,5 +161,5 @@ vector<MythGLTexture*> MythMediaCodecInterop::Acquire(MythRenderOpenGL *Context,

// Update texture
m_surfaceTexture.callMethod<void>("updateTexImage");
return m_openglTextures[DUMMY_TEXTURE_ID];
return m_openglTextures[DUMMY_INTEROP_ID];
}
2 changes: 2 additions & 0 deletions mythtv/libs/libmythtv/mythopenglinterop.h
Expand Up @@ -14,6 +14,8 @@ using std::vector;

class VideoColourSpace;

#define DUMMY_INTEROP_ID 1

class MythOpenGLInterop : public ReferenceCounter
{
public:
Expand Down
21 changes: 15 additions & 6 deletions mythtv/libs/libmythtv/mythvaapiinterop.cpp
Expand Up @@ -18,8 +18,6 @@
.arg(__FILE__).arg( __LINE__).arg(va_status) \
.arg(vaErrorStr(va_status)))

#define GLX_DUMMY_ID 1

/*! \brief Return an 'interoperability' method that is supported by the current render device.
*
* DRM interop is the preferred option as it is copy free but requires EGL. To
Expand Down Expand Up @@ -157,6 +155,7 @@ VASurfaceID MythVAAPIInterop::VerifySurface(MythRenderOpenGL *Context, VideoFram

MythVAAPIInteropGLX::MythVAAPIInteropGLX(MythRenderOpenGL *Context, Type InteropType)
: MythVAAPIInterop(Context, InteropType),
m_lastSurface(0),
m_vaapiPictureAttributes(nullptr),
m_vaapiPictureAttributeCount(0),
m_vaapiHueBase(0),
Expand Down Expand Up @@ -427,17 +426,22 @@ vector<MythGLTexture*> MythVAAPIInteropGLXCopy::Acquire(MythRenderOpenGL *Contex
}

result.push_back(texture);
m_openglTextures.insert(GLX_DUMMY_ID, result);
m_openglTextures.insert(DUMMY_INTEROP_ID, result);
}

if (m_openglTextures.isEmpty() || !m_glxSurface)
return result;
result = m_openglTextures[GLX_DUMMY_ID];
result = m_openglTextures[DUMMY_INTEROP_ID];

// Pause frame - no need to update the same frame
if (m_lastSurface == id)
return result;

// Copy surface to texture
INIT_ST;
va_status = vaCopySurfaceGLX(m_vaDisplay, m_glxSurface, id, GetFlagsForFrame(Frame, Scan));
CHECK_ST;
m_lastSurface = id;
return result;
}

Expand Down Expand Up @@ -561,12 +565,16 @@ vector<MythGLTexture*> MythVAAPIInteropGLXPixmap::Acquire(MythRenderOpenGL *Cont
if (!texture)
return result;
result.push_back(texture);
m_openglTextures.insert(GLX_DUMMY_ID, result);
m_openglTextures.insert(DUMMY_INTEROP_ID, result);
}

if (m_openglTextures.isEmpty() || !m_glxPixmap || !m_pixmap)
return result;
result = m_openglTextures[GLX_DUMMY_ID];
result = m_openglTextures[DUMMY_INTEROP_ID];

// Pause frame - no need to update the same frame
if (m_lastSurface == id)
return result;

// Copy the surface to the texture
INIT_ST;
Expand All @@ -587,6 +595,7 @@ vector<MythGLTexture*> MythVAAPIInteropGLXPixmap::Acquire(MythRenderOpenGL *Cont
m_glxBindTexImageEXT(glxdisplay, m_glxPixmap, GLX_FRONT_EXT, nullptr);
m_context->glBindTexture(QOpenGLTexture::Target2D, 0);
}
m_lastSurface = id;
return result;
}

Expand Down
1 change: 1 addition & 0 deletions mythtv/libs/libmythtv/mythvaapiinterop.h
Expand Up @@ -54,6 +54,7 @@ class MythVAAPIInteropGLX : public QObject, public MythVAAPIInterop
void InitPictureAttributes(VideoColourSpace *ColourSpace);

protected:
VASurfaceID m_lastSurface;
VADisplayAttribute *m_vaapiPictureAttributes;
int m_vaapiPictureAttributeCount;
int m_vaapiHueBase;
Expand Down
8 changes: 3 additions & 5 deletions mythtv/libs/libmythtv/mythvtbinterop.cpp
Expand Up @@ -50,8 +50,6 @@ MythVTBInterop::~MythVTBInterop()
{
}

#define DUMMY_TEXTURE_ID 1

vector<MythGLTexture*> MythVTBInterop::Acquire(MythRenderOpenGL *Context,
VideoColourSpace *ColourSpace,
VideoFrame *Frame,
Expand Down Expand Up @@ -106,12 +104,12 @@ vector<MythGLTexture*> MythVTBInterop::Acquire(MythRenderOpenGL *Context,

if (result.size() != static_cast<uint>(planes))
LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create all textures");
m_openglTextures.insert(DUMMY_TEXTURE_ID, result);
m_openglTextures.insert(DUMMY_INTEROP_ID, result);
}

if (m_openglTextures.contains(DUMMY_TEXTURE_ID))
if (m_openglTextures.contains(DUMMY_INTEROP_ID))
{
result = m_openglTextures[DUMMY_TEXTURE_ID];
result = m_openglTextures[DUMMY_INTEROP_ID];
m_context->glEnable(QOpenGLTexture::Target2D);

// Update textures
Expand Down
52 changes: 51 additions & 1 deletion mythtv/libs/libmythtv/videoout_opengl.cpp
Expand Up @@ -143,6 +143,11 @@ bool VideoOutputOpenGL::CreateVideoResources(void)

void VideoOutputOpenGL::DestroyCPUResources(void)
{
vbuffers.begin_lock(kVideoBuffer_pause);
while (vbuffers.Size(kVideoBuffer_pause))
vbuffers.DiscardFrame(vbuffers.Tail(kVideoBuffer_pause));
vbuffers.end_lock();

DiscardFrames(true);
vbuffers.DeleteBuffers();
vbuffers.Reset();
Expand Down Expand Up @@ -503,6 +508,12 @@ void VideoOutputOpenGL::PrepareFrame(VideoFrame *Frame, FrameScanType Scan, OSD
topfieldfirst = Frame->top_field_first;
dummy = Frame->dummy;
}
else if ((m_openGLVideoType == OpenGLVideo::kGLInterop) && !(codec_sw_copy(video_codec_id)))
{
// see VideoOutputOpenGL::DoneDisplayingFrame
if (vbuffers.Size(kVideoBuffer_pause))
Frame = vbuffers.Tail(kVideoBuffer_pause);
}

m_render->BindFramebuffer(nullptr);
if (db_letterbox_colour == kLetterBoxColour_Gray25)
Expand Down Expand Up @@ -613,6 +624,42 @@ void VideoOutputOpenGL::PrepareFrame(VideoFrame *Frame, FrameScanType Scan, OSD
m_render->logDebugMarker(LOC + "PREPARE_FRAME_END");
}

/*! \brief Release a video frame back into the decoder pool.
*
* Software frames do not need a pause frame as OpenGLVideo
* holds a copy of the last frame in its input textures. So
* just release the frame.
*
* Hardware frames hold the underlying interop class and
* hence access to the video texture. We cannot access them
* without a frame so retain the most recent frame by removing
* it from the 'used' queue and adding it to the 'pause' queue.
*/
void VideoOutputOpenGL::DoneDisplayingFrame(VideoFrame *Frame)
{
if (!Frame)
return;

if ((m_openGLVideoType == OpenGLVideo::kGLInterop) && !(codec_sw_copy(video_codec_id)))
{
vbuffers.begin_lock(kVideoBuffer_pause);
while (vbuffers.Size(kVideoBuffer_pause))
{
VideoFrame* frame = vbuffers.Dequeue(kVideoBuffer_pause);
if (frame != Frame)
VideoOutput::DoneDisplayingFrame(frame);
}
vbuffers.Enqueue(kVideoBuffer_pause, Frame);
if(vbuffers.Contains(kVideoBuffer_used, Frame))
vbuffers.Remove(kVideoBuffer_used, Frame);
vbuffers.end_lock();
}
else
{
VideoOutput::DoneDisplayingFrame(Frame);
}
}

void VideoOutputOpenGL::Show(FrameScanType /*scan*/)
{
if (m_render && !IsErrored())
Expand Down Expand Up @@ -672,7 +719,10 @@ void VideoOutputOpenGL::UpdatePauseFrame(int64_t &DisplayTimecode)
VideoFrame *used = vbuffers.Head(kVideoBuffer_used);
if (used)
{
m_openGLVideo->UpdateInputFrame(used);
if ((m_openGLVideoType == OpenGLVideo::kGLInterop) && !(codec_sw_copy(video_codec_id)))
DoneDisplayingFrame(used);
else
m_openGLVideo->UpdateInputFrame(used);
DisplayTimecode = used->disp_timecode;
}
else
Expand Down
1 change: 1 addition & 0 deletions mythtv/libs/libmythtv/videoout_opengl.h
Expand Up @@ -50,6 +50,7 @@ class VideoOutputOpenGL : public VideoOutput
bool SetupVisualisation(AudioPlayer *Audio, MythRender *Render, const QString &Name) override;
QStringList GetVisualiserList(void) override;
bool StereoscopicModesAllowed(void) const override { return true; }
void DoneDisplayingFrame(VideoFrame *Frame) override;

protected:
bool CreateGPUResources(void);
Expand Down

0 comments on commit d00954f

Please sign in to comment.