Output video using OMX Raspberry Pi

This commit is contained in:
Iwan Timmer 2015-04-29 21:56:04 +02:00
parent de74c11828
commit f231f0a8e0
6 changed files with 3406 additions and 28 deletions

View File

@ -5,9 +5,12 @@ SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
aux_source_directory(./common/limelight-common SRC_LIST)
aux_source_directory(./common/limelight-common/OpenAES SRC_LIST)
aux_source_directory(./ilclient SRC_LIST)
aux_source_directory(./src SRC_LIST)
include_directories(./common)
include_directories(./ilclient)
add_executable(moonlight ${SRC_LIST})
set_property(TARGET moonlight PROPERTY C_STANDARD 11)
@ -18,9 +21,11 @@ find_package(OpenSSL)
find_package(EXPAT)
find_package(ALSA)
find_package(Opus)
find_package(Broadcom)
find_package(PkgConfig REQUIRED)
pkg_check_modules(EVDEV libevdev)
include_directories(./moonlight-common-c ${OPUS_INCLUDE_DIRS} ${EVDEV_INCLUDE_DIRS})
target_link_libraries (moonlight PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES} ${EXPAT_LIBRARIES} ${EVDEV_LIBRARIES} ${ALSA_LIBRARY} ${OPUS_LIBRARY})
include_directories(./moonlight-common-c ${OPUS_INCLUDE_DIRS} ${EVDEV_INCLUDE_DIRS} ${BROADCOM_INCLUDE_DIRS})
target_link_libraries (moonlight PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES} ${EXPAT_LIBRARIES} ${EVDEV_LIBRARIES} ${ALSA_LIBRARY} ${OPUS_LIBRARY} ${BROADCOM_LIBRARIES})
set_property(TARGET moonlight PROPERTY COMPILE_DEFINITIONS ${BROADCOM_DEFINITIONS})

36
cmake/FindBroadcom.cmake Normal file
View File

@ -0,0 +1,36 @@
find_path(BROADCOM_INCLUDE_DIR
NAMES bcm_host.h
DOC "Broadcom include directory"
PATHS /opt/vc/include)
mark_as_advanced(BCM_INCLUDE_DIR)
find_library(VCOS_LIBRARY
NAMES libvcos.so
DOC "Path to VCOS Library"
PATHS /opt/vc/lib)
mark_as_advanced(VCOS_LIBRARY)
find_library(VCHIQ_LIBRARY
NAMES libvchiq_arm.so
DOC "Path to VCHIQ Library"
PATHS /opt/vc/lib)
mark_as_advanced(VCHIQ_LIBRARY)
find_library(OPENMAXIL_LIBRARY
NAMES libopenmaxil.so
DOC "Path to OpenMAX IL Library"
PATHS /opt/vc/lib)
mark_as_advanced(OPENMAXIL_LIBRARY)
find_library(BCM_HOST_LIBRARY
NAMES libbcm_host.so
DOC "Path to Broadcom Host Library"
PATHS /opt/vc/lib)
mark_as_advanced(BCM_HOST_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_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)

1836
ilclient/ilclient.c Normal file

File diff suppressed because it is too large Load Diff

1039
ilclient/ilclient.h Normal file

File diff suppressed because it is too large Load Diff

308
ilclient/ilcore.c Normal file
View File

@ -0,0 +1,308 @@
/*
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.
*/
/*
* \file
*
* \brief Host core implementation.
*/
#include <stdio.h>
#include <stdarg.h>
//includes
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "IL/OMX_Component.h"
#include "interface/vcos/vcos.h"
#include "interface/vmcs_host/vcilcs.h"
#include "interface/vmcs_host/vchost.h"
#include "interface/vmcs_host/vcilcs_common.h"
static int coreInit = 0;
static int nActiveHandles = 0;
static ILCS_SERVICE_T *ilcs_service = NULL;
static VCOS_MUTEX_T lock;
static VCOS_ONCE_T once = VCOS_ONCE_INIT;
/* Atomic creation of lock protecting shared state */
static void initOnce(void)
{
VCOS_STATUS_T status;
status = vcos_mutex_create(&lock, VCOS_FUNCTION);
vcos_demand(status == VCOS_SUCCESS);
}
/* OMX_Init */
OMX_ERRORTYPE OMX_APIENTRY OMX_Init(void)
{
VCOS_STATUS_T status;
OMX_ERRORTYPE err = OMX_ErrorNone;
status = vcos_once(&once, initOnce);
vcos_demand(status == VCOS_SUCCESS);
vcos_mutex_lock(&lock);
if(coreInit == 0)
{
// we need to connect via an ILCS connection to VideoCore
VCHI_INSTANCE_T initialise_instance;
VCHI_CONNECTION_T *connection;
ILCS_CONFIG_T config;
vc_host_get_vchi_state(&initialise_instance, &connection);
vcilcs_config(&config);
ilcs_service = ilcs_init((VCHIQ_INSTANCE_T) initialise_instance, (void **) &connection, &config, 0);
if(ilcs_service == NULL)
{
err = OMX_ErrorHardware;
goto end;
}
coreInit = 1;
}
else
coreInit++;
end:
vcos_mutex_unlock(&lock);
return err;
}
/* OMX_Deinit */
OMX_ERRORTYPE OMX_APIENTRY OMX_Deinit(void)
{
if(coreInit == 0) // || (coreInit == 1 && nActiveHandles > 0))
return OMX_ErrorNotReady;
vcos_mutex_lock(&lock);
coreInit--;
if(coreInit == 0)
{
// we need to teardown the ILCS connection to VideoCore
ilcs_deinit(ilcs_service);
ilcs_service = NULL;
}
vcos_mutex_unlock(&lock);
return OMX_ErrorNone;
}
/* OMX_ComponentNameEnum */
OMX_ERRORTYPE OMX_APIENTRY OMX_ComponentNameEnum(
OMX_OUT OMX_STRING cComponentName,
OMX_IN OMX_U32 nNameLength,
OMX_IN OMX_U32 nIndex)
{
if(ilcs_service == NULL)
return OMX_ErrorBadParameter;
return vcil_out_component_name_enum(ilcs_get_common(ilcs_service), cComponentName, nNameLength, nIndex);
}
/* OMX_GetHandle */
OMX_ERRORTYPE OMX_APIENTRY OMX_GetHandle(
OMX_OUT OMX_HANDLETYPE* pHandle,
OMX_IN OMX_STRING cComponentName,
OMX_IN OMX_PTR pAppData,
OMX_IN OMX_CALLBACKTYPE* pCallBacks)
{
OMX_ERRORTYPE eError;
OMX_COMPONENTTYPE *pComp;
OMX_HANDLETYPE hHandle = 0;
if (pHandle == NULL || cComponentName == NULL || pCallBacks == NULL || ilcs_service == NULL)
{
if(pHandle)
*pHandle = NULL;
return OMX_ErrorBadParameter;
}
{
pComp = (OMX_COMPONENTTYPE *)malloc(sizeof(OMX_COMPONENTTYPE));
if (!pComp)
{
vcos_assert(0);
return OMX_ErrorInsufficientResources;
}
memset(pComp, 0, sizeof(OMX_COMPONENTTYPE));
hHandle = (OMX_HANDLETYPE)pComp;
pComp->nSize = sizeof(OMX_COMPONENTTYPE);
pComp->nVersion.nVersion = OMX_VERSION;
eError = vcil_out_create_component(ilcs_get_common(ilcs_service), hHandle, cComponentName);
if (eError == OMX_ErrorNone) {
// Check that all function pointers have been filled in.
// All fields should be non-zero.
int i;
uint32_t *p = (uint32_t *) pComp;
for(i=0; i<sizeof(OMX_COMPONENTTYPE)>>2; i++)
if(*p++ == 0)
eError = OMX_ErrorInvalidComponent;
if(eError != OMX_ErrorNone && pComp->ComponentDeInit)
pComp->ComponentDeInit(hHandle);
}
if (eError == OMX_ErrorNone) {
eError = pComp->SetCallbacks(hHandle,pCallBacks,pAppData);
if (eError != OMX_ErrorNone)
pComp->ComponentDeInit(hHandle);
}
if (eError == OMX_ErrorNone) {
*pHandle = hHandle;
}
else {
*pHandle = NULL;
free(pComp);
}
}
if (eError == OMX_ErrorNone) {
vcos_mutex_lock(&lock);
nActiveHandles++;
vcos_mutex_unlock(&lock);
}
return eError;
}
/* OMX_FreeHandle */
OMX_ERRORTYPE OMX_APIENTRY OMX_FreeHandle(
OMX_IN OMX_HANDLETYPE hComponent)
{
OMX_ERRORTYPE eError = OMX_ErrorNone;
OMX_COMPONENTTYPE *pComp;
if (hComponent == NULL || ilcs_service == NULL)
return OMX_ErrorBadParameter;
pComp = (OMX_COMPONENTTYPE*)hComponent;
if (ilcs_service == NULL)
return OMX_ErrorBadParameter;
eError = (pComp->ComponentDeInit)(hComponent);
if (eError == OMX_ErrorNone) {
vcos_mutex_lock(&lock);
--nActiveHandles;
vcos_mutex_unlock(&lock);
free(pComp);
}
vcos_assert(nActiveHandles >= 0);
return eError;
}
/* OMX_SetupTunnel */
OMX_ERRORTYPE OMX_APIENTRY OMX_SetupTunnel(
OMX_IN OMX_HANDLETYPE hOutput,
OMX_IN OMX_U32 nPortOutput,
OMX_IN OMX_HANDLETYPE hInput,
OMX_IN OMX_U32 nPortInput)
{
OMX_ERRORTYPE eError = OMX_ErrorNone;
OMX_COMPONENTTYPE *pCompIn, *pCompOut;
OMX_TUNNELSETUPTYPE oTunnelSetup;
if ((hOutput == NULL && hInput == NULL) || ilcs_service == NULL)
return OMX_ErrorBadParameter;
oTunnelSetup.nTunnelFlags = 0;
oTunnelSetup.eSupplier = OMX_BufferSupplyUnspecified;
pCompOut = (OMX_COMPONENTTYPE*)hOutput;
if (hOutput){
eError = pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, hInput, nPortInput, &oTunnelSetup);
}
if (eError == OMX_ErrorNone && hInput) {
pCompIn = (OMX_COMPONENTTYPE*)hInput;
eError = pCompIn->ComponentTunnelRequest(hInput, nPortInput, hOutput, nPortOutput, &oTunnelSetup);
if (eError != OMX_ErrorNone && hOutput) {
/* cancel tunnel request on output port since input port failed */
pCompOut->ComponentTunnelRequest(hOutput, nPortOutput, NULL, 0, NULL);
}
}
return eError;
}
/* OMX_GetComponentsOfRole */
OMX_ERRORTYPE OMX_GetComponentsOfRole (
OMX_IN OMX_STRING role,
OMX_INOUT OMX_U32 *pNumComps,
OMX_INOUT OMX_U8 **compNames)
{
OMX_ERRORTYPE eError = OMX_ErrorNone;
*pNumComps = 0;
return eError;
}
/* OMX_GetRolesOfComponent */
OMX_ERRORTYPE OMX_GetRolesOfComponent (
OMX_IN OMX_STRING compName,
OMX_INOUT OMX_U32 *pNumRoles,
OMX_OUT OMX_U8 **roles)
{
OMX_ERRORTYPE eError = OMX_ErrorNone;
*pNumRoles = 0;
return eError;
}
/* OMX_GetDebugInformation */
OMX_ERRORTYPE OMX_GetDebugInformation (
OMX_OUT OMX_STRING debugInfo,
OMX_INOUT OMX_S32 *pLen)
{
if(ilcs_service == NULL)
return OMX_ErrorBadParameter;
return vcil_out_get_debug_information(ilcs_get_common(ilcs_service), debugInfo, pLen);
}
/* File EOF */

View File

@ -1,32 +1,123 @@
/*
* This file is part of Moonlight Embedded.
*
* Copyright (C) 2015 Iwan Timmer
*
* Moonlight is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Moonlight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
*/
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 OpenMAX IL though the ilcient helper library
// Based upon video decode example from the Raspberry Pi firmware
#include "video.h"
#include <stdio.h>
#include "bcm_host.h"
#include "ilclient.h"
static FILE* fd;
static const char* fileName = "fake.h264";
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static TUNNEL_T tunnel[2];
static COMPONENT_T *list[3];
static ILCLIENT_T *client;
static COMPONENT_T *video_decode = NULL, *video_scheduler = NULL, *video_render = NULL;
static OMX_BUFFERHEADERTYPE *buf;
static unsigned char *dest;
static int port_settings_changed;
static int first_packet;
static void decoder_renderer_setup(int width, int height, int redrawRate, void* context, int drFlags) {
printf("decoder_renderer_setup %dx%d %dfps\n", width, height, redrawRate);
fd = fopen(fileName, "w");
bcm_host_init();
OMX_VIDEO_PARAM_PORTFORMATTYPE format;
OMX_TIME_CONFIG_CLOCKSTATETYPE cstate;
COMPONENT_T *clock = NULL;
unsigned int data_len = 0;
int packet_size = 80<<10;
memset(list, 0, sizeof(list));
memset(tunnel, 0, sizeof(tunnel));
if((client = ilclient_init()) == NULL) {
fprintf(stderr, "Can't initialize video\n");
exit(EXIT_FAILURE);
}
if(OMX_Init() != OMX_ErrorNone) {
fprintf(stderr, "Can't initialize OMX\n");
exit(EXIT_FAILURE);
}
// create video_decode
if(ilclient_create_component(client, &video_decode, "video_decode", ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0){
fprintf(stderr, "Can't create video decode\n");
exit(EXIT_FAILURE);
}
list[0] = video_decode;
// create video_render
if(ilclient_create_component(client, &video_render, "video_render", ILCLIENT_DISABLE_ALL_PORTS) != 0){
fprintf(stderr, "Can't create video renderer\n");
exit(EXIT_FAILURE);
}
list[1] = video_render;
set_tunnel(tunnel, video_decode, 131, video_render, 90);
ilclient_change_component_state(video_decode, OMX_StateIdle);
memset(&format, 0, sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE));
format.nSize = sizeof(OMX_VIDEO_PARAM_PORTFORMATTYPE);
format.nVersion.nVersion = OMX_VERSION;
format.nPortIndex = 130;
format.eCompressionFormat = OMX_VIDEO_CodingAVC;
OMX_PARAM_DATAUNITTYPE unit;
memset(&unit, 0, sizeof(OMX_PARAM_DATAUNITTYPE));
unit.nSize = sizeof(OMX_PARAM_DATAUNITTYPE);
unit.nVersion.nVersion = OMX_VERSION;
unit.nPortIndex = 130;
unit.eUnitType = OMX_DataUnitCodedPicture;
unit.eEncapsulationType = OMX_DataEncapsulationElementaryStream;
if(OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamVideoPortFormat, &format) == OMX_ErrorNone &&
OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamBrcmDataUnit, &unit) == OMX_ErrorNone &&
ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0) {
port_settings_changed = 0;
first_packet = 1;
ilclient_change_component_state(video_decode, OMX_StateExecuting);
} else {
fprintf(stderr, "Can't setup video\n");
exit(EXIT_FAILURE);
}
}
static void decoder_renderer_start() {
@ -34,20 +125,83 @@ static void decoder_renderer_start() {
}
static void decoder_renderer_stop() {
printf("decoder_renderer_stop\n");
int status = 0;
buf->nFilledLen = 0;
buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS;
if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(list[0]), buf) != OMX_ErrorNone){
fprintf(stderr, "Can't empty video buffer\n");
return;
}
// need to flush the renderer to allow video_decode to disable its input port
ilclient_flush_tunnels(tunnel, 0);
ilclient_disable_port_buffers(list[0], 130, NULL, NULL, NULL);
}
static void decoder_renderer_release() {
printf("decoder_renderer_release\n");
fclose(fd);
ilclient_disable_tunnel(tunnel);
ilclient_teardown_tunnels(tunnel);
ilclient_state_transition(list, OMX_StateIdle);
ilclient_state_transition(list, OMX_StateLoaded);
ilclient_cleanup_components(list);
OMX_Deinit();
ilclient_destroy(client);
}
static int decoder_renderer_submit_decode_unit(PDECODE_UNIT decodeUnit) {
if((buf = ilclient_get_input_buffer(video_decode, 130, 1)) == NULL){
fprintf(stderr, "Can't get video buffer\n");
exit(EXIT_FAILURE);
}
// feed data and wait until we get port settings changed
dest = buf->pBuffer;
buf->nFilledLen = 0;
buf->nOffset = 0;
buf->nFlags = OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS;
if(first_packet) {
buf->nFlags = OMX_BUFFERFLAG_STARTTIME;
first_packet = 0;
}
PLENTRY entry = decodeUnit->bufferList;
while (entry != NULL) {
fwrite(entry->data, entry->length, 1, fd);
memcpy(dest, entry->data, entry->length);
buf->nFilledLen += entry->length;
dest += entry->length;
entry = entry->next;
}
if(port_settings_changed == 0 &&
((buf->nFilledLen > 0 && ilclient_remove_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1) == 0) ||
(buf->nFilledLen == 0 && ilclient_wait_for_event(video_decode, OMX_EventPortSettingsChanged, 131, 0, 0, 1,
ILCLIENT_EVENT_ERROR | ILCLIENT_PARAMETER_CHANGED, 10000) == 0))) {
port_settings_changed = 1;
if(ilclient_setup_tunnel(tunnel, 0, 0) != 0){
fprintf(stderr, "Can't setup video\n");
exit(EXIT_FAILURE);
}
ilclient_change_component_state(video_render, OMX_StateExecuting);
}
if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(video_decode), buf) != OMX_ErrorNone){
fprintf(stderr, "Can't empty video buffer\n");
exit(EXIT_FAILURE);
}
return DR_OK;
}