Add MMAL video decoder

This commit is contained in:
Iwan Timmer 2020-03-17 22:17:34 +01:00
parent 70e54ef300
commit 922cd29adc
5 changed files with 326 additions and 7 deletions

View File

@ -85,9 +85,16 @@ if(BROADCOM_FOUND)
aux_source_directory(./third_party/ilclient ILCLIENT_SRC_LIST)
add_library(moonlight-pi SHARED ./src/video/pi.c ./src/audio/omx.c ${ILCLIENT_SRC_LIST})
target_include_directories(moonlight-pi PRIVATE ./third_party/ilclient ${BROADCOM_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR} ${OPUS_INCLUDE_DIRS})
target_link_libraries(moonlight-pi gamestream ${BROADCOM_LIBRARIES} ${OPUS_LIBRARY})
set_property(TARGET moonlight-pi PROPERTY COMPILE_DEFINITIONS ${BROADCOM_DEFINITIONS})
target_link_libraries(moonlight-pi gamestream ${BROADCOM_OMX_LIBRARIES} ${OPUS_LIBRARY})
set_property(TARGET moonlight-pi PROPERTY COMPILE_DEFINITIONS ${BROADCOM_OMX_DEFINITIONS})
install(TARGETS moonlight-pi DESTINATION ${CMAKE_INSTALL_LIBDIR})
list(APPEND MOONLIGHT_DEFINITIONS HAVE_MMAL)
list(APPEND MOONLIGHT_OPTIONS MMAL)
add_library(moonlight-mmal SHARED ./src/video/mmal.c)
target_include_directories(moonlight-mmal PRIVATE ${BROADCOM_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
target_link_libraries(moonlight-mmal gamestream ${BROADCOM_MMAL_LIBRARIES})
install(TARGETS moonlight-mmal DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
if(FREESCALE_FOUND)

View File

@ -28,9 +28,28 @@ find_library(BCM_HOST_LIBRARY
PATHS /opt/vc/lib)
mark_as_advanced(BCM_HOST_LIBRARY)
find_library(MMAL_CORE_LIBRARY
NAMES libmmal_core.so
DOC "Path to MMAL Core Library"
PATHS /opt/vc/lib)
mark_as_advanced(MMAL_CORE_LIBRARY)
find_library(MMAL_UTIL_LIBRARY
NAMES libmmal_util.so
DOC "Path to MMAL Util Library"
PATHS /opt/vc/lib)
mark_as_advanced(MMAL_UTIL_LIBRARY)
find_library(MMAL_VC_CLIENT_LIBRARY
NAMES libmmal_vc_client.so
DOC "Path to MMAL Client Library"
PATHS /opt/vc/lib)
mark_as_advanced(MMAL_VC_CLIENT_LIBRARY)
include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Broadcom DEFAULT_MSG BROADCOM_INCLUDE_DIR VCOS_LIBRARY VCHIQ_LIBRARY OPENMAXIL_LIBRARY BCM_HOST_LIBRARY)
set(BROADCOM_LIBRARIES ${BCM_HOST_LIBRARY} ${OPENMAXIL_LIBRARY} ${VCHIQ_LIBRARY} ${VCOS_LIBRARY})
set(BROADCOM_OMX_LIBRARIES ${BCM_HOST_LIBRARY} ${OPENMAXIL_LIBRARY} ${VCHIQ_LIBRARY} ${VCOS_LIBRARY})
set(BROADCOM_MMAL_LIBRARIES ${BCM_HOST_LIBRARY} ${VCOS_LIBRARY} ${MMAL_CORE_LIBRARY} ${MMAL_UTIL_LIBRARY} ${MMAL_VC_CLIENT_LIBRARY})
set(BROADCOM_INCLUDE_DIRS ${BROADCOM_INCLUDE_DIR} ${BROADCOM_INCLUDE_DIR}/interface/vmcs_host/linux ${BROADCOM_INCLUDE_DIR}/interface/vcos/pthreads)
set(BROADCOM_DEFINITIONS USE_VCHIQ_ARM HAVE_LIBOPENMAX=2 OMX OMX_SKIP64BIT USE_EXTERNAL_OMX HAVE_LIBBCM_HOST USE_EXTERNAL_LIBBCM_HOST)
set(BROADCOM_OMX_DEFINITIONS USE_VCHIQ_ARM HAVE_LIBOPENMAX=2 OMX OMX_SKIP64BIT USE_EXTERNAL_OMX HAVE_LIBBCM_HOST USE_EXTERNAL_LIBBCM_HOST)

View File

@ -52,6 +52,13 @@ enum platform platform_check(char* name) {
return PI;
}
#endif
#ifdef HAVE_MMAL
if (std || strcmp(name, "mmal") == 0) {
void *handle = dlopen("libmoonlight-mmal.so", RTLD_NOW | RTLD_GLOBAL);
if (handle != NULL && dlsym(RTLD_DEFAULT, "bcm_host_init") != NULL)
return MMAL;
}
#endif
#ifdef HAVE_AML
if (std || strcmp(name, "aml") == 0) {
void *handle = dlopen("libmoonlight-aml.so", RTLD_NOW | RTLD_GLOBAL);
@ -101,7 +108,7 @@ void platform_start(enum platform system) {
blank_fb("/sys/class/graphics/fb1/blank", true);
break;
#endif
#ifdef HAVE_PI
#if defined(HAVE_PI) | defined(HAVE_MMAL)
case PI:
blank_fb("/sys/class/graphics/fb0/blank", true);
break;
@ -117,7 +124,7 @@ void platform_stop(enum platform system) {
blank_fb("/sys/class/graphics/fb1/blank", false);
break;
#endif
#ifdef HAVE_PI
#if defined(HAVE_PI) | defined(HAVE_MMAL)
case PI:
blank_fb("/sys/class/graphics/fb0/blank", false);
break;
@ -151,6 +158,10 @@ DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system) {
case PI:
return (PDECODER_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "decoder_callbacks_pi");
#endif
#ifdef HAVE_MMAL
case MMAL:
return (PDECODER_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "decoder_callbacks_mmal");
#endif
#ifdef HAVE_AML
case AML:
return (PDECODER_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "decoder_callbacks_aml");
@ -199,6 +210,8 @@ char* platform_name(enum platform system) {
switch(system) {
case PI:
return "Raspberry Pi (Broadcom)";
case MMAL:
return "Raspberry Pi (Broadcom) MMAL";
case IMX:
return "i.MX6 (MXC Vivante)";
case AML:

View File

@ -26,7 +26,7 @@
#define IS_EMBEDDED(SYSTEM) SYSTEM != SDL
enum platform { NONE, SDL, X11, X11_VDPAU, X11_VAAPI, PI, IMX, AML, RK, FAKE };
enum platform { NONE, SDL, X11, X11_VDPAU, X11_VAAPI, PI, MMAL, IMX, AML, RK, FAKE };
enum platform platform_check(char*);
PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system);

280
src/video/mmal.c Normal file
View File

@ -0,0 +1,280 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Video decode on Raspberry Pi using MMAL
// Based upon example code from the Raspberry Pi
#include <Limelight.h>
#include <sps.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <bcm_host.h>
#include <interface/mmal/mmal.h>
#include <interface/mmal/util/mmal_default_components.h>
#include <interface/mmal/util/mmal_util_params.h>
#include <interface/mmal/util/mmal_util.h>
#include <interface/mmal/vc/mmal_vc_api.h>
#include <interface/vcos/vcos.h>
#define MAX_DECODE_UNIT_SIZE 262144
#define ALIGN(x, a) (((x)+(a)-1)&~((a)-1))
static VCOS_SEMAPHORE_T semaphore;
static MMAL_COMPONENT_T *decoder = NULL, *renderer = NULL;
static MMAL_POOL_T *pool_in = NULL, *pool_out = NULL;
static void input_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) {
mmal_buffer_header_release(buf);
if (port == decoder->input[0])
vcos_semaphore_post(&semaphore);
}
static void control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) {
if (buf->cmd == MMAL_EVENT_ERROR) {
MMAL_STATUS_T status = *(uint32_t *) buf->data;
fprintf(stderr, "Video decode error MMAL_EVENT_ERROR:%d\n", status);
}
mmal_buffer_header_release(buf);
}
static void output_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) {
if (mmal_port_send_buffer(renderer->input[0], buf) != MMAL_SUCCESS) {
fprintf(stderr, "Can't display decoded frame\n");
mmal_buffer_header_release(buf);
}
}
static int decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
MMAL_STATUS_T status;
if (videoFormat != VIDEO_FORMAT_H264) {
fprintf(stderr, "Video format not supported\n");
return -1;
}
bcm_host_init();
mmal_vc_init();
gs_sps_init(width, height);
vcos_semaphore_create(&semaphore, "video_decoder", 1);
if (mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &decoder) != MMAL_SUCCESS) {
fprintf(stderr, "Can't create decoder\n");
return -2;
}
MMAL_ES_FORMAT_T *format_in = decoder->input[0]->format;
format_in->type = MMAL_ES_TYPE_VIDEO;
format_in->encoding = MMAL_ENCODING_H264;
format_in->es->video.width = ALIGN(width, 32);
format_in->es->video.height = ALIGN(height, 16);
format_in->es->video.crop.width = width;
format_in->es->video.crop.height = height;
format_in->es->video.frame_rate.num = redrawRate;
format_in->es->video.frame_rate.den = 1;
format_in->es->video.par.num = 1;
format_in->es->video.par.den = 1;
format_in->flags = MMAL_ES_FORMAT_FLAG_FRAMED;
if (mmal_port_format_commit(decoder->input[0]) != MMAL_SUCCESS) {
fprintf(stderr, "Can't commit input format to decoder\n");
return -3;
}
decoder->input[0]->buffer_num = 5;
decoder->input[0]->buffer_size = MAX_DECODE_UNIT_SIZE;
pool_in = mmal_port_pool_create(decoder->input[0], decoder->input[0]->buffer_num, decoder->output[0]->buffer_size);
MMAL_ES_FORMAT_T *format_out = decoder->output[0]->format;
format_out->encoding = MMAL_ENCODING_OPAQUE;
if (mmal_port_format_commit(decoder->output[0]) != MMAL_SUCCESS) {
fprintf(stderr, "Can't commit output format to decoder\n");
return -3;
}
decoder->output[0]->buffer_num = 3;
decoder->output[0]->buffer_size = decoder->output[0]->buffer_size_recommended;
pool_out = mmal_port_pool_create(decoder->output[0], decoder->output[0]->buffer_num, decoder->output[0]->buffer_size);
if (mmal_port_enable(decoder->control, control_callback) != MMAL_SUCCESS) {
fprintf(stderr, "Can't enable control port\n");
return -4;
}
if (mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &renderer) != MMAL_SUCCESS) {
fprintf(stderr, "Can't create renderer\n");
return -5;
}
format_in = renderer->input[0]->format;
format_in->encoding = MMAL_ENCODING_OPAQUE;
format_in->es->video.width = width;
format_in->es->video.height = height;
format_in->es->video.crop.x = 0;
format_in->es->video.crop.y = 0;
format_in->es->video.crop.width = width;
format_in->es->video.crop.height = height;
if (mmal_port_format_commit(renderer->input[0]) != MMAL_SUCCESS) {
fprintf(stderr, "Can't set output format\n");
return -6;
}
MMAL_DISPLAYREGION_T param;
param.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
param.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
param.set = MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_NUM | MMAL_DISPLAY_SET_FULLSCREEN;
param.layer = 128;
param.display_num = 0;
param.fullscreen = true;
if (mmal_port_parameter_set(renderer->input[0], &param.hdr) != MMAL_SUCCESS) {
fprintf(stderr, "Can't set parameters\n");
return -7;
}
if (mmal_port_enable(renderer->control, control_callback) != MMAL_SUCCESS) {
fprintf(stderr, "Can't enable control port\n");
return -8;
}
if (mmal_component_enable(renderer) != MMAL_SUCCESS) {
fprintf(stderr, "Can't enable renderer\n");
return -9;
}
if (mmal_port_enable(renderer->input[0], input_callback) != MMAL_SUCCESS) {
fprintf(stderr, "Can't enable renderer input port\n");
return -10;
}
if (mmal_port_enable(decoder->input[0], input_callback) != MMAL_SUCCESS) {
fprintf(stderr, "Can't enable decoder input port\n");
return -11;
}
if (mmal_port_enable(decoder->output[0], output_callback) != MMAL_SUCCESS) {
fprintf(stderr, "Can't enable decoder output port\n");
return -12;
}
if (mmal_component_enable(decoder) != MMAL_SUCCESS) {
fprintf(stderr, "Can't enable decoder\n");
return -13;
}
return 0;
}
static void decoder_renderer_cleanup() {
if (decoder)
mmal_component_destroy(decoder);
if (renderer)
mmal_component_destroy(renderer);
if (pool_in)
mmal_pool_destroy(pool_in);
if (pool_out)
mmal_pool_destroy(pool_out);
vcos_semaphore_delete(&semaphore);
}
static int decoder_renderer_submit_decode_unit(PDECODE_UNIT decodeUnit) {
MMAL_STATUS_T status;
MMAL_BUFFER_HEADER_T *buf = NULL;
PLENTRY entry = decodeUnit->bufferList;
bool first_entry = false;
while (entry != NULL) {
if (buf == NULL) {
vcos_semaphore_wait(&semaphore);
if ((buf = mmal_queue_get(pool_in->queue)) != NULL) {
buf->flags = 0;
buf->offset = 0;
buf->pts = buf->dts = MMAL_TIME_UNKNOWN;
} else {
fprintf(stderr, "Video buffer full\n");
return DR_NEED_IDR;
}
}
if (entry->bufferType != BUFFER_TYPE_PICDATA)
buf->flags |= MMAL_BUFFER_HEADER_FLAG_CONFIG;
else if (!first_entry) {
buf->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_START;
first_entry = true;
}
if (entry->bufferType == BUFFER_TYPE_SPS)
gs_sps_fix(entry, GS_SPS_BITSTREAM_FIXUP, buf->data, &buf->length);
else {
if (entry->length + buf->length > buf->alloc_size) {
fprintf(stderr, "Video decoder buffer too small\n");
mmal_buffer_header_release(buf);
return DR_NEED_IDR;
}
memcpy(buf->data + buf->length, entry->data, entry->length);
buf->length += entry->length;
}
if (entry->bufferType != BUFFER_TYPE_PICDATA || entry->next == NULL || entry->next->bufferType != BUFFER_TYPE_PICDATA) {
buf->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END;
if ((status = mmal_port_send_buffer(decoder->input[0], buf)) != MMAL_SUCCESS) {
mmal_buffer_header_release(buf);
return DR_NEED_IDR;
}
buf = NULL;
}
entry = entry->next;
}
//Send available output buffers to decoder
while ((buf = mmal_queue_get(pool_out->queue))) {
if ((status = mmal_port_send_buffer(decoder->output[0], buf)) != MMAL_SUCCESS)
mmal_buffer_header_release(buf);
}
return DR_OK;
}
DECODER_RENDERER_CALLBACKS decoder_callbacks_mmal = {
.setup = decoder_renderer_setup,
.cleanup = decoder_renderer_cleanup,
.submitDecodeUnit = decoder_renderer_submit_decode_unit,
.capabilities = CAPABILITY_DIRECT_SUBMIT,
};