From 9c76700f747f8c935801f5b8b3e54626eb459c51 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Tue, 16 Apr 2019 01:20:21 -0700 Subject: [PATCH] Add MMAL renderer for Raspberry Pi --- app/app.pro | 12 ++ app/streaming/video/ffmpeg-renderers/mmal.cpp | 145 ++++++++++++++++++ app/streaming/video/ffmpeg-renderers/mmal.h | 25 +++ app/streaming/video/ffmpeg.cpp | 21 +++ 4 files changed, 203 insertions(+) create mode 100644 app/streaming/video/ffmpeg-renderers/mmal.cpp create mode 100644 app/streaming/video/ffmpeg-renderers/mmal.h diff --git a/app/app.pro b/app/app.pro index 1399e9d9..5402c4e6 100644 --- a/app/app.pro +++ b/app/app.pro @@ -94,6 +94,11 @@ unix:!macx { packagesExist(vdpau) { CONFIG += libvdpau } + + packagesExist(mmal) { + PKGCONFIG += mmal + CONFIG += mmal + } } } win32 { @@ -213,6 +218,13 @@ libvdpau { SOURCES += streaming/video/ffmpeg-renderers/vdpau.cpp HEADERS += streaming/video/ffmpeg-renderers/vdpau.h } +mmal { + message(MMAL renderer selected) + + DEFINES += HAVE_MMAL + SOURCES += streaming/video/ffmpeg-renderers/mmal.cpp + HEADERS += streaming/video/ffmpeg-renderers/mmal.h +} config_SL { message(Steam Link build configuration selected) diff --git a/app/streaming/video/ffmpeg-renderers/mmal.cpp b/app/streaming/video/ffmpeg-renderers/mmal.cpp new file mode 100644 index 00000000..4cafcb19 --- /dev/null +++ b/app/streaming/video/ffmpeg-renderers/mmal.cpp @@ -0,0 +1,145 @@ +#include "mmal.h" + +#include "streaming/streamutils.h" +#include "streaming/session.h" + +#include + +MmalRenderer::MmalRenderer() + : m_Renderer(nullptr), + m_InputPort(nullptr) +{ +} + +MmalRenderer::~MmalRenderer() +{ + if (m_InputPort != nullptr) { + mmal_port_disable(m_InputPort); + } + + if (m_Renderer != nullptr) { + mmal_component_destroy(m_Renderer); + } +} + +bool MmalRenderer::prepareDecoderContext(AVCodecContext*) +{ + /* Nothing to do */ + + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Using MMAL renderer"); + + return true; +} + +bool MmalRenderer::initialize(PDECODER_PARAMETERS params) +{ + MMAL_STATUS_T status; + + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &m_Renderer); + if (status != MMAL_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "mmal_component_create() failed: %x (%s)", + status, mmal_status_to_string(status)); + return false; + } + + m_InputPort = m_Renderer->input[0]; + + m_InputPort->format->encoding = MMAL_ENCODING_OPAQUE; + m_InputPort->format->es->video.width = params->width; + m_InputPort->format->es->video.height = params->height; + m_InputPort->format->es->video.crop.x = 0; + m_InputPort->format->es->video.crop.y = 0; + m_InputPort->format->es->video.crop.width = params->width; + m_InputPort->format->es->video.crop.height = params->height; + status = mmal_port_format_commit(m_InputPort); + if (status != MMAL_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "mmal_port_format_commit() failed: %x (%s)", + status, mmal_status_to_string(status)); + return false; + } + + status = mmal_component_enable(m_Renderer); + if (status != MMAL_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "mmal_component_enable() failed: %x (%s)", + status, mmal_status_to_string(status)); + return false; + } + + { + MMAL_DISPLAYREGION_T dr; + + dr.hdr.id = MMAL_PARAMETER_DISPLAYREGION; + dr.hdr.size = sizeof(MMAL_DISPLAYREGION_T); + + dr.set = MMAL_DISPLAY_SET_LAYER; + dr.layer = 128; + + dr.set |= MMAL_DISPLAY_SET_ALPHA; + dr.alpha = 255; + + dr.set |= MMAL_DISPLAY_SET_FULLSCREEN; + dr.fullscreen = (SDL_GetWindowFlags(params->window) & SDL_WINDOW_FULLSCREEN) != 0; + + { + SDL_Rect src, dst; + src.x = src.y = 0; + src.w = params->width; + src.h = params->height; + dst.x = dst.y = 0; + SDL_GetWindowSize(params->window, &dst.w, &dst.h); + + StreamUtils::scaleSourceToDestinationSurface(&src, &dst); + + dr.set |= MMAL_DISPLAY_SET_DEST_RECT; + dr.dest_rect.x = dst.x; + dr.dest_rect.y = dst.y; + dr.dest_rect.width = dst.w; + dr.dest_rect.height = dst.h; + } + } + + status = mmal_port_enable(m_InputPort, InputPortCallback); + if (status != MMAL_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "mmal_port_enable() failed: %x (%s)", + status, mmal_status_to_string(status)); + return false; + } + + return true; +} + +void MmalRenderer::InputPortCallback(MMAL_PORT_T*, MMAL_BUFFER_HEADER_T* buffer) +{ + mmal_buffer_header_release(buffer); +} + +enum AVPixelFormat MmalRenderer::getPreferredPixelFormat(int videoFormat) +{ + // Opaque MMAL buffers + SDL_assert(videoFormat == VIDEO_FORMAT_H264); + return AV_PIX_FMT_MMAL; +} + +void MmalRenderer::renderFrame(AVFrame* frame) +{ + MMAL_BUFFER_HEADER_T* buffer = (MMAL_BUFFER_HEADER_T*)frame->data[3]; + MMAL_STATUS_T status; + + status = mmal_port_send_buffer(m_InputPort, buffer); + if (status != MMAL_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "mmal_port_send_buffer() failed: %x (%s)", + status, mmal_status_to_string(status)); + } + else { + // Prevent the buffer from being freed during av_frame_free() + // until rendering is complete. The reference is dropped in + // InputPortCallback(). + mmal_buffer_header_acquire(buffer); + } +} diff --git a/app/streaming/video/ffmpeg-renderers/mmal.h b/app/streaming/video/ffmpeg-renderers/mmal.h new file mode 100644 index 00000000..87c189ae --- /dev/null +++ b/app/streaming/video/ffmpeg-renderers/mmal.h @@ -0,0 +1,25 @@ +#pragma once + +#include "renderer.h" + +#include +#include +#include +#include + +class MmalRenderer : public IFFmpegRenderer { +public: + MmalRenderer(); + virtual ~MmalRenderer() override; + virtual bool initialize(PDECODER_PARAMETERS params) override; + virtual bool prepareDecoderContext(AVCodecContext* context) override; + virtual void renderFrame(AVFrame* frame) override; + virtual enum AVPixelFormat getPreferredPixelFormat(int videoFormat) override; + +private: + static void InputPortCallback(MMAL_PORT_T* port, MMAL_BUFFER_HEADER_T* buffer); + + MMAL_COMPONENT_T* m_Renderer; + MMAL_PORT_T* m_InputPort; +}; + diff --git a/app/streaming/video/ffmpeg.cpp b/app/streaming/video/ffmpeg.cpp index 478c3e53..5bdcc6cf 100644 --- a/app/streaming/video/ffmpeg.cpp +++ b/app/streaming/video/ffmpeg.cpp @@ -23,6 +23,10 @@ #include "ffmpeg-renderers/vdpau.h" #endif +#ifdef HAVE_MMAL +#include "ffmpeg-renderers/mmal.h" +#endif + // This is gross but it allows us to use sizeof() #include "ffmpeg_videosamples.cpp" @@ -463,6 +467,23 @@ bool FFmpegVideoDecoder::initialize(PDECODER_PARAMETERS params) } } +#ifdef HAVE_MMAL + if ((params->videoFormat & VIDEO_FORMAT_MASK_H264) && + (params->vds != StreamingPreferences::VDS_FORCE_SOFTWARE)) { + AVCodec* mmalDecoder = avcodec_find_decoder_by_name("h264_mmal"); + if (mmalDecoder) { + m_BackendRenderer = new MmalRenderer(); + if (m_BackendRenderer->initialize(params) && + completeInitialization(mmalDecoder, params, m_TestOnly)) { + return true; + } + else { + reset(); + } + } + } +#endif + // We must fall back to a non-hardware accelerated decoder as // all other possibilities have been exhausted. m_HwDecodeCfg = nullptr;