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
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_unref(m_LastFrame);
av_frame_move_ref(m_LastFrame, frame); 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 initialize(PDECODER_PARAMETERS params) override;
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override; virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) override;
virtual void renderFrame(AVFrame* frame) override; virtual void renderFrame(AVFrame* frame) override;
virtual bool testRenderFrame(AVFrame* frame) override;
virtual void notifyOverlayUpdated(Overlay::OverlayType) override; virtual void notifyOverlayUpdated(Overlay::OverlayType) override;
virtual bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override; virtual bool isPixelFormatSupported(int videoFormat, enum AVPixelFormat pixelFormat) override;
virtual AVPixelFormat getPreferredPixelFormat(int videoFormat) override; virtual AVPixelFormat getPreferredPixelFormat(int videoFormat) override;

View file

@ -86,6 +86,13 @@ public:
virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) = 0; virtual bool prepareDecoderContext(AVCodecContext* context, AVDictionary** options) = 0;
virtual void renderFrame(AVFrame* frame) = 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() { virtual bool needsTestFrame() {
// No test frame required by default // No test frame required by default
return false; return false;

View file

@ -193,17 +193,22 @@ void FFmpegVideoDecoder::reset()
} }
} }
bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params) bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params, bool eglOnly)
{ {
if (eglOnly) {
#ifdef HAVE_EGL #ifdef HAVE_EGL
if (m_BackendRenderer->canExportEGL()) { if (m_BackendRenderer->canExportEGL()) {
m_FrontendRenderer = new EGLRenderer(m_BackendRenderer); m_FrontendRenderer = new EGLRenderer(m_BackendRenderer);
if (m_FrontendRenderer->initialize(params)) { if (m_FrontendRenderer->initialize(params)) {
return true; return true;
}
delete m_FrontendRenderer;
m_FrontendRenderer = nullptr;
} }
delete m_FrontendRenderer;
}
#endif #endif
// If we made it here, we failed to create the EGLRenderer
return false;
}
if (m_BackendRenderer->isDirectRenderingSupported()) { if (m_BackendRenderer->isDirectRenderingSupported()) {
// The backend renderer can render to the display // The backend renderer can render to the display
@ -221,13 +226,13 @@ bool FFmpegVideoDecoder::createFrontendRenderer(PDECODER_PARAMETERS params)
return true; 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 // In test-only mode, we should only see test frames
SDL_assert(!m_TestOnly || testFrame); SDL_assert(!m_TestOnly || testFrame);
// Create the frontend renderer based on the capabilities of the backend renderer // Create the frontend renderer based on the capabilities of the backend renderer
if (!createFrontendRenderer(params)) { if (!createFrontendRenderer(params, eglOnly)) {
return false; 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); av_frame_free(&frame);
if (err < 0) { if (err < 0) {
char errorstring[512]; char errorstring[512];
@ -555,42 +568,48 @@ bool FFmpegVideoDecoder::tryInitializeRenderer(AVCodec* decoder,
const AVCodecHWConfig* hwConfig, const AVCodecHWConfig* hwConfig,
std::function<IFFmpegRenderer*()> createRendererFunc) std::function<IFFmpegRenderer*()> createRendererFunc)
{ {
m_BackendRenderer = createRendererFunc();
m_HwDecodeCfg = hwConfig; m_HwDecodeCfg = hwConfig;
if (m_BackendRenderer != nullptr && // i == 0 - Indirect via EGL frontend with zero-copy DMA-BUF passing
m_BackendRenderer->initialize(params) && // i == 1 - Direct rendering or indirect via SDL read-back
completeInitialization(decoder, params, m_TestOnly || m_BackendRenderer->needsTestFrame())) { for (int i = 0; i < 2; i++) {
if (m_TestOnly) { SDL_assert(m_BackendRenderer == nullptr);
// This decoder is only for testing capabilities, so don't bother if ((m_BackendRenderer = createRendererFunc()) != nullptr &&
// creating a usable renderer m_BackendRenderer->initialize(params) &&
return true; 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
if (m_BackendRenderer->needsTestFrame()) { // creating a usable renderer
// The test worked, so now let's initialize it for real
reset();
if ((m_BackendRenderer = createRendererFunc()) != nullptr &&
m_BackendRenderer->initialize(params) &&
completeInitialization(decoder, params, false)) {
return true; return true;
} }
else {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, if (m_BackendRenderer->needsTestFrame()) {
"Decoder failed to initialize after successful test"); // The test worked, so now let's initialize it for real
reset(); reset();
if ((m_BackendRenderer = createRendererFunc()) != nullptr &&
m_BackendRenderer->initialize(params) &&
completeInitialization(decoder, params, false, i == 0 /* EGL */)) {
return true;
}
else {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION,
"Decoder failed to initialize after successful test");
reset();
}
}
else {
// No test required. Good to go now.
return true;
} }
} }
else { else {
// No test required. Good to go now. // Failed to initialize, so keep looking
return true; reset();
} }
} }
else {
// Failed to initialize or test frame failed, so keep looking
reset();
}
// reset() must be called before we reach this point!
SDL_assert(m_BackendRenderer == nullptr);
return false; return false;
} }

View file

@ -26,7 +26,7 @@ public:
virtual IFFmpegRenderer* getBackendRenderer(); virtual IFFmpegRenderer* getBackendRenderer();
private: 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); void stringifyVideoStats(VIDEO_STATS& stats, char* output);
@ -34,7 +34,7 @@ private:
void addVideoStats(VIDEO_STATS& src, VIDEO_STATS& dst); 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, bool tryInitializeRendererForDecoderByName(const char* decoderName,
PDECODER_PARAMETERS params); PDECODER_PARAMETERS params);