Skip to content

Commit

Permalink
MythVAAPIInterop: A little optimisation for VPP deinterlacing
Browse files Browse the repository at this point in the history
- track the current field we are expecting for double rate deinterlacers
and only ask for the second field when required
- at startup, add the frame a few times to prime the deinterlacer. This
ensures we never return frames from the main frame pool and hence do not
use OpenGL textures for frames we won't see again - hence saving a
little memory (typically 2 frames/textures saved)
  • Loading branch information
mark-kendall committed Oct 16, 2019
1 parent 8587b1e commit 23421d2
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 42 deletions.
95 changes: 53 additions & 42 deletions mythtv/libs/libmythtv/mythvaapiinterop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ void MythVAAPIInterop::DestroyDeinterlacer(void)
m_filterSource = nullptr;
m_deinterlacer = DEINT_NONE;
m_deinterlacer2x = false;
m_firstField = true;
m_lastFilteredFrame = 0;
m_lastFilteredFrameCount = 0;
av_buffer_unref(&m_vppFramesContext);
Expand Down Expand Up @@ -1198,13 +1199,6 @@ bool MythVAAPIInterop::SetupDeinterlacer(MythDeintType Deinterlacer, bool Double
return ret >= 0;
}

/*! \brief Perform VPP (VAAPI Post Processing) deinterlacing
*
* \note For advanced deinterlacers, the deinterlacer will typically return the
* current frame twice until it has enough reference frames. These are then mapped
* to an OpenGLTexture when using DRM but never used again. For best memory management
* we should not retain these textures. This is not relevant for GLX methods.
*/
VASurfaceID MythVAAPIInterop::Deinterlace(VideoFrame *Frame, VASurfaceID Current, FrameScanType Scan)
{
VASurfaceID result = Current;
Expand Down Expand Up @@ -1327,47 +1321,64 @@ VASurfaceID MythVAAPIInterop::Deinterlace(VideoFrame *Frame, VASurfaceID Current

Frame->deinterlace_inuse = m_deinterlacer | DEINT_DRIVER;
Frame->deinterlace_inuse2x = m_deinterlacer2x;
while (true)

// 'pump' the filter with frames until it starts returning usefull output.
// This minimises discontinuities at start up (where we would otherwise
// show a couple of progressive frames first) and memory consumption for the DRM
// interop as we only cache OpenGL textures for the deinterlacer's frame
// pool.
int retries = 3;
while ((result == Current) && retries--)
{
int ret = 0;
MythAVFrame sinkframe;
sinkframe->format = AV_PIX_FMT_VAAPI;
ret = av_buffersink_get_frame(m_filterSink, sinkframe);
if (ret >= 0)
while (true)
{
// we have a filtered frame
result = m_lastFilteredFrame = static_cast<VASurfaceID>(reinterpret_cast<uintptr_t>(sinkframe->data[3]));
m_lastFilteredFrameCount = Frame->frameCounter;
break;
}
else if (ret != AVERROR(EAGAIN))
break;
int ret = 0;
MythAVFrame sinkframe;
sinkframe->format = AV_PIX_FMT_VAAPI;

// add another frame
MythAVFrame sourceframe;
sourceframe->top_field_first = Frame->interlaced_reversed ? !Frame->top_field_first : Frame->top_field_first;
sourceframe->interlaced_frame = 1;
sourceframe->data[3] = Frame->buf;
sourceframe->buf[0] = av_buffer_ref(reinterpret_cast<AVBufferRef*>(Frame->priv[0]));
sourceframe->width = m_filterWidth;
sourceframe->height = m_filterHeight;
sourceframe->format = AV_PIX_FMT_VAAPI;
ret = av_buffersrc_add_frame(m_filterSource, sourceframe);
sourceframe->data[3] = nullptr;
sourceframe->buf[0] = nullptr;
if (ret < 0)
break;
// only ask for a frame if we are expecting another
if (m_deinterlacer2x && !m_firstField)
{
ret = av_buffersink_get_frame(m_filterSink, sinkframe);
if (ret >= 0)
{
// we have a filtered frame
result = m_lastFilteredFrame = static_cast<VASurfaceID>(reinterpret_cast<uintptr_t>(sinkframe->data[3]));
m_lastFilteredFrameCount = Frame->frameCounter;
m_firstField = true;
break;
}
else if (ret != AVERROR(EAGAIN))
break;
}

// try again
ret = av_buffersink_get_frame(m_filterSink, sinkframe);
if (ret >= 0)
{
// we have a filtered frame
result = m_lastFilteredFrame = static_cast<VASurfaceID>(reinterpret_cast<uintptr_t>(sinkframe->data[3]));
m_lastFilteredFrameCount = Frame->frameCounter;
// add another frame
MythAVFrame sourceframe;
sourceframe->top_field_first = Frame->interlaced_reversed ? !Frame->top_field_first : Frame->top_field_first;
sourceframe->interlaced_frame = 1;
sourceframe->data[3] = Frame->buf;
sourceframe->buf[0] = av_buffer_ref(reinterpret_cast<AVBufferRef*>(Frame->priv[0]));
sourceframe->width = m_filterWidth;
sourceframe->height = m_filterHeight;
sourceframe->format = AV_PIX_FMT_VAAPI;
ret = av_buffersrc_add_frame(m_filterSource, sourceframe);
sourceframe->data[3] = nullptr;
sourceframe->buf[0] = nullptr;
if (ret < 0)
break;

// try again
ret = av_buffersink_get_frame(m_filterSink, sinkframe);
if (ret >= 0)
{
// we have a filtered frame
result = m_lastFilteredFrame = static_cast<VASurfaceID>(reinterpret_cast<uintptr_t>(sinkframe->data[3]));
m_lastFilteredFrameCount = Frame->frameCounter;
m_firstField = false;
break;
}
break;
}
break;
}
}
return result;
Expand Down
1 change: 1 addition & 0 deletions mythtv/libs/libmythtv/mythvaapiinterop.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class MythVAAPIInterop : public MythOpenGLInterop

MythDeintType m_deinterlacer { DEINT_NONE };
bool m_deinterlacer2x { false };
bool m_firstField { true };
AVBufferRef *m_vppFramesContext { nullptr };
AVFilterContext *m_filterSink { nullptr };
AVFilterContext *m_filterSource { nullptr };
Expand Down

0 comments on commit 23421d2

Please sign in to comment.