Allow fallback from EGL to direct on EGLImage export failure

This commit is contained in:
Cameron Gutman 2021-03-22 22:51:29 -05:00
parent 4a8c9ad17f
commit e74753bec1
5 changed files with 81 additions and 36 deletions

View File

@ -836,3 +836,21 @@ void EGLRenderer::renderFrame(AVFrame* frame)
av_frame_unref(m_LastFrame);
av_frame_move_ref(m_LastFrame, frame);
}
bool EGLRenderer::testRenderFrame(AVFrame* frame)
{
EGLImage imgs[EGL_MAX_PLANES];
// Make sure we can get working EGLImages from the backend renderer.
// Some devices (Raspberry Pi) will happily decode into DRM formats that
// its own GL implementation won't accept in eglCreateImage().
ssize_t plane_count = m_Backend->exportEGLImages(frame, m_EGLDisplay, imgs);
if (plane_count <= 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Backend failed to export EGL image for test frame");
return false;
}
m_Backend->freeEGLImages(m_EGLDisplay, imgs);
return true;
}

View File

@ -12,6 +12,7 @@ public:
virtual bool initialize(PDECODER_PARAMETERS params) override;
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
virtual void renderFrame(AVFrame* frame) override;
virtual bool testRenderFrame(AVFrame* frame) override;
virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
virtual bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override;
virtual AVPixelFormat getPreferredPixelFormat(int videoFormat) override;

View File

@ -86,6 +86,13 @@ public:
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) = 0;
virtual void renderFrame(AVFrame* frame) = 0;
virtual bool testRenderFrame(AVFrame*) {
// If the renderer doesn't provide an explicit test routine,
// we will always assume that any returned AVFrame can be
// rendered successfully.
return true;
}
virtual bool needsTestFrame() {
// No test frame required by default
return false;

View File

@ -193,8 +193,9 @@ void FFmpegVideoDecoder::reset()
}
}
bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params)
bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params, bool eglOnly)
{
if (eglOnly) {
#ifdef HAVE_EGL
if (m_BackendRenderer->canExportEGL()) {
m_FrontendRenderer = new EGLRenderer(m_BackendRenderer);
@ -202,8 +203,12 @@ bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params)
return true;
}
delete m_FrontendRenderer;
m_FrontendRenderer = nullptr;
}
#endif
// If we made it here, we failed to create the EGLRenderer
return false;
}
if (m_BackendRenderer->isDirectRenderingSupported()) {
// The backend renderer can render to the display
@ -221,13 +226,13 @@ bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params)
return true;
}
bool FFmpegVideoDecoder::completeInitialization(AVCodec* decoder, PDECODER_PARAMETERS params, bool testFrame)
bool FFmpegVideoDecoder::completeInitialization(AVCodec* decoder, PDECODER_PARAMETERS params, bool testFrame, bool eglOnly)
{
// In test-only mode, we should only see test frames
SDL_assert(!m_TestOnly || testFrame);
// Create the frontend renderer based on the capabilities of the backend renderer
if (!createFrontendRenderer(params)) {
if (!createFrontendRenderer(params, eglOnly)) {
return false;
}
@ -365,6 +370,14 @@ bool FFmpegVideoDecoder::completeInitialization(AVCodec* decoder, PDECODER_PARAM
}
}
// Allow the renderer to do any validation it wants on this frame
if (!m_FrontendRenderer->testRenderFrame(frame)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Render test failed");
av_frame_free(&frame);
return false;
}
av_frame_free(&frame);
if (err < 0) {
char errorstring[512];
@ -555,12 +568,15 @@ bool FFmpegVideoDecoder::tryInitializeRenderer(AVCodec* decoder,
const AVCodecHWConfig* hwConfig,
std::function<IFFmpegRenderer*()> createRendererFunc)
{
m_BackendRenderer = createRendererFunc();
m_HwDecodeCfg = hwConfig;
if (m_BackendRenderer != nullptr &&
// i == 0 - Indirect via EGL frontend with zero-copy DMA-BUF passing
// i == 1 - Direct rendering or indirect via SDL read-back
for (int i = 0; i < 2; i++) {
SDL_assert(m_BackendRenderer == nullptr);
if ((m_BackendRenderer = createRendererFunc()) != nullptr &&
m_BackendRenderer->initialize(params) &&
completeInitialization(decoder, params, m_TestOnly || m_BackendRenderer->needsTestFrame())) {
completeInitialization(decoder, params, m_TestOnly || m_BackendRenderer->needsTestFrame(), i == 0 /* EGL */)) {
if (m_TestOnly) {
// This decoder is only for testing capabilities, so don't bother
// creating a usable renderer
@ -572,7 +588,7 @@ bool FFmpegVideoDecoder::tryInitializeRenderer(AVCodec* decoder,
reset();
if ((m_BackendRenderer = createRendererFunc()) != nullptr &&
m_BackendRenderer->initialize(params) &&
completeInitialization(decoder, params, false)) {
completeInitialization(decoder, params, false, i == 0 /* EGL */)) {
return true;
}
else {
@ -587,10 +603,13 @@ bool FFmpegVideoDecoder::tryInitializeRenderer(AVCodec* decoder,
}
}
else {
// Failed to initialize or test frame failed, so keep looking
// Failed to initialize, so keep looking
reset();
}
}
// reset() must be called before we reach this point!
SDL_assert(m_BackendRenderer == nullptr);
return false;
}

View File

@ -26,7 +26,7 @@ public:
virtual IFFmpegRenderer* getBackendRenderer();
private:
bool completeInitialization(AVCodec* decoder, PDECODER_PARAMETERS params, bool testFrame);
bool completeInitialization(AVCodec* decoder, PDECODER_PARAMETERS params, bool testFrame, bool eglOnly);
void stringifyVideoStats(VIDEO_STATS& stats, char* output);
@ -34,7 +34,7 @@ private:
void addVideoStats(VIDEO_STATS& src, VIDEO_STATS& dst);
bool createFrontendRenderer(PDECODER_PARAMETERS params);
bool createFrontendRenderer(PDECODER_PARAMETERS params, bool eglOnly);
bool tryInitializeRendererForDecoderByName(const char* decoderName,
PDECODER_PARAMETERS params);