From 8de4fe1cf31ef32ab7cc1ece00042e92b5c85d15 Mon Sep 17 00:00:00 2001 From: Unknownforce351 Date: Fri, 4 Mar 2016 13:10:31 -0600 Subject: [PATCH 1/2] Initial OMX Support --- CMakeLists.txt | 6 +- src/audio.h | 3 + src/audio/alsa.c | 2 - src/audio/omx.c | 205 +++++++++++++++++++++++++++++++++++++++++++++++ src/config.c | 1 + src/main.c | 2 +- src/platform.c | 4 + 7 files changed, 217 insertions(+), 6 deletions(-) create mode 100644 src/audio/omx.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bcf54f..e2b6251 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,9 +105,9 @@ if(BROADCOM_FOUND) list(APPEND MOONLIGHT_DEFINITIONS HAVE_PI) list(APPEND MOONLIGHT_OPTIONS PI) aux_source_directory(./third_party/ilclient ILCLIENT_SRC_LIST) - add_library(moonlight-pi SHARED ./src/video/pi.c ${ILCLIENT_SRC_LIST}) - target_include_directories(moonlight-pi PRIVATE ./third_party/ilclient ${BROADCOM_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR}) - target_link_libraries(moonlight-pi gamestream ${BROADCOM_LIBRARIES}) + 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}) install(TARGETS moonlight-pi DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() diff --git a/src/audio.h b/src/audio.h index 8236e2a..de7aece 100644 --- a/src/audio.h +++ b/src/audio.h @@ -31,3 +31,6 @@ extern AUDIO_RENDERER_CALLBACKS audio_callbacks_sdl; extern AUDIO_RENDERER_CALLBACKS audio_callbacks_pulse; bool audio_pulse_init(); #endif +#ifdef HAVE_PI +extern AUDIO_RENDERER_CALLBACKS audio_callbacks_omx; +#endif diff --git a/src/audio/alsa.c b/src/audio/alsa.c index 3999b2c..2815375 100644 --- a/src/audio/alsa.c +++ b/src/audio/alsa.c @@ -28,8 +28,6 @@ #define MAX_CHANNEL_COUNT 6 #define FRAME_SIZE 240 -const char* audio_device = "sysdefault"; - static snd_pcm_t *handle; static OpusMSDecoder* decoder; static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT]; diff --git a/src/audio/omx.c b/src/audio/omx.c new file mode 100644 index 0000000..b5ae4bd --- /dev/null +++ b/src/audio/omx.c @@ -0,0 +1,205 @@ +/* + * 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 . + */ + +#include "../audio.h" + +#include + +#include +#include "bcm_host.h" +#include "ilclient.h" + +#define MAX_CHANNEL_COUNT 6 +#define FRAME_SIZE 240 + +static OpusMSDecoder* decoder; +ILCLIENT_T* handle; +COMPONENT_T* component; +static OMX_BUFFERHEADERTYPE *buf; +static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT]; +static int channelCount; + +static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { + int rc, error; + OMX_ERRORTYPE err; + unsigned char omxMapping[6]; + char* componentName = "audio_render"; + + channelCount = opusConfig->channelCount; + /* The supplied mapping array has order: FL-FR-C-LFE-RL-RR + * OMX expects the order: FL-FR-LFE-C-RL-RR + * We need copy the mapping locally and swap the channels around. + */ + memcpy(omxMapping, opusConfig->mapping, sizeof(omxMapping)); + if (opusConfig->channelCount > 2) { + omxMapping[2] = opusConfig->mapping[3]; + omxMapping[3] = opusConfig->mapping[2]; + } + + decoder = opus_multistream_decoder_create(opusConfig->sampleRate, + opusConfig->channelCount, + opusConfig->streams, + opusConfig->coupledStreams, + omxMapping, + &rc); + + handle = ilclient_init(); + if (handle == NULL) { + fprintf(stderr, "IL client init failed\n"); + exit(1); + } + + if (ilclient_create_component(handle, &component, componentName, ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0) { + fprintf(stderr, "Component create failed\n"); + exit(1); + } + + if (ilclient_change_component_state(component, OMX_StateIdle)!= 0) { + fprintf(stderr, "Couldn't change state to Idle\n"); + exit(1); + } + + // must be before we enable buffers + OMX_AUDIO_PARAM_PORTFORMATTYPE audioPortFormat; + memset(&audioPortFormat, 0, sizeof(OMX_AUDIO_PARAM_PORTFORMATTYPE)); + audioPortFormat.nSize = sizeof(OMX_AUDIO_PARAM_PORTFORMATTYPE); + audioPortFormat.nVersion.nVersion = OMX_VERSION; + + audioPortFormat.nPortIndex = 100; + + OMX_GetParameter(ilclient_get_handle(component), OMX_IndexParamAudioPortFormat, &audioPortFormat); + + audioPortFormat.eEncoding = OMX_AUDIO_CodingPCM; + OMX_SetParameter(ilclient_get_handle(component), OMX_IndexParamAudioPortFormat, &audioPortFormat); + + OMX_AUDIO_PARAM_PCMMODETYPE sPCMMode; + + memset(&sPCMMode, 0, sizeof(OMX_AUDIO_PARAM_PCMMODETYPE)); + sPCMMode.nSize = sizeof(OMX_AUDIO_PARAM_PCMMODETYPE); + sPCMMode.nVersion.nVersion = OMX_VERSION; + sPCMMode.nPortIndex = 100; + sPCMMode.nChannels = channelCount; + sPCMMode.eNumData = OMX_NumericalDataSigned; + sPCMMode.eEndian = OMX_EndianLittle; + sPCMMode.nSamplingRate = opusConfig->sampleRate; + sPCMMode.bInterleaved = OMX_TRUE; + sPCMMode.nBitPerSample = 16; + sPCMMode.ePCMMode = OMX_AUDIO_PCMModeLinear; + + switch(channelCount) { + case 1: + sPCMMode.eChannelMapping[0] = OMX_AUDIO_ChannelCF; + break; + case 8: + sPCMMode.eChannelMapping[7] = OMX_AUDIO_ChannelRS; + case 7: + sPCMMode.eChannelMapping[6] = OMX_AUDIO_ChannelLS; + case 6: + sPCMMode.eChannelMapping[5] = OMX_AUDIO_ChannelRR; + case 5: + sPCMMode.eChannelMapping[4] = OMX_AUDIO_ChannelLR; + case 4: + sPCMMode.eChannelMapping[3] = OMX_AUDIO_ChannelLFE; + case 3: + sPCMMode.eChannelMapping[2] = OMX_AUDIO_ChannelCF; + case 2: + sPCMMode.eChannelMapping[1] = OMX_AUDIO_ChannelRF; + sPCMMode.eChannelMapping[0] = OMX_AUDIO_ChannelLF; + break; + } + + err = OMX_SetParameter(ilclient_get_handle(component), OMX_IndexParamAudioPcm, &sPCMMode); + if(err != OMX_ErrorNone){ + fprintf(stderr, "PCM mode unsupported\n"); + return; + } + OMX_CONFIG_BRCMAUDIODESTINATIONTYPE arDest; + + if (audio_device && strlen(audio_device) < sizeof(arDest.sName)) { + memset(&arDest, 0, sizeof(OMX_CONFIG_BRCMAUDIODESTINATIONTYPE)); + arDest.nSize = sizeof(OMX_CONFIG_BRCMAUDIODESTINATIONTYPE); + arDest.nVersion.nVersion = OMX_VERSION; + + strcpy((char *)arDest.sName, audio_device); + + err = OMX_SetParameter(ilclient_get_handle(component), OMX_IndexConfigBrcmAudioDestination, &arDest); + if (err != OMX_ErrorNone) { + fprintf(stderr, "Error on setting audio destination\nomx option must be set to hdmi or local\n"); + exit(1); + } + } + + // input port + ilclient_enable_port_buffers(component, 100, NULL, NULL, NULL); + ilclient_enable_port(component, 100); + + err = ilclient_change_component_state(component, OMX_StateExecuting); + if (err < 0) { + fprintf(stderr, "Couldn't change state to Executing\n"); + exit(1); + } +} + +static void omx_renderer_cleanup() { + if (decoder != NULL) + opus_multistream_decoder_destroy(decoder); + if (handle != NULL) { + if((buf = ilclient_get_input_buffer(component, 100, 1)) == NULL){ + fprintf(stderr, "Can't get audio buffer\n"); + exit(EXIT_FAILURE); + } + + buf->nFilledLen = 0; + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(component), buf) != OMX_ErrorNone){ + fprintf(stderr, "Can't empty audio buffer\n"); + return; + } + + ilclient_disable_port_buffers(component, 100, NULL, NULL, NULL); + ilclient_change_component_state(component, OMX_StateIdle); + ilclient_change_component_state(component, OMX_StateLoaded); + } +} + +static void omx_renderer_decode_and_play_sample(char* data, int length) { + int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, FRAME_SIZE, 0); + if (decodeLen > 0) { + buf = ilclient_get_input_buffer(component, 100, 1); + buf->nOffset = 0; + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + int bufLength = decodeLen * sizeof(short) * channelCount; + memcpy(buf->pBuffer, pcmBuffer, bufLength); + buf->nFilledLen = bufLength; + int r = OMX_EmptyThisBuffer(ilclient_get_handle(component), buf); + if (r != OMX_ErrorNone) { + fprintf(stderr, "Empty buffer error\n"); + } + } else { + printf("Opus error from decode: %d\n", decodeLen); + } +} + +AUDIO_RENDERER_CALLBACKS audio_callbacks_omx = { + .init = omx_renderer_init, + .cleanup = omx_renderer_cleanup, + .decodeAndPlaySample = omx_renderer_decode_and_play_sample, + .capabilities = CAPABILITY_DIRECT_SUBMIT, +}; diff --git a/src/config.c b/src/config.c index 6bfef0e..fa65fd8 100644 --- a/src/config.c +++ b/src/config.c @@ -38,6 +38,7 @@ bool inputAdded = false; static bool mapped = true; +const char* audio_device = "sysdefault"; static struct option long_options[] = { {"720", no_argument, NULL, 'a'}, diff --git a/src/main.c b/src/main.c index 6f67ca9..0314496 100644 --- a/src/main.c +++ b/src/main.c @@ -152,7 +152,7 @@ static void help() { printf("\n I/O options\n\n"); printf("\t-mapping \t\tUse as gamepad mapping configuration file (use before -input)\n"); printf("\t-input \t\tUse as input. Can be used multiple times\n"); - printf("\t-audio \t\tUse as ALSA audio output device (default sysdefault)\n"); + printf("\t-audio \t\tUse as audio output device (default sysdefault)\n"); printf("\t-forcehw \t\tTry to use video hardware acceleration\n"); #endif printf("\nUse Ctrl+Alt+Shift+Q to exit streaming session\n\n"); diff --git a/src/platform.c b/src/platform.c index d9f01e2..7c2d44f 100644 --- a/src/platform.c +++ b/src/platform.c @@ -99,6 +99,10 @@ AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system) { case SDL: return &audio_callbacks_sdl; #endif + #ifdef HAVE_PI + case PI: + return (PAUDIO_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "audio_callbacks_omx"); + #endif default: #ifdef HAVE_PULSE if (audio_pulse_init()) From 68e18a20671da36fc3370ad087557f6f51b67604 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Fri, 25 Mar 2016 11:12:20 +0100 Subject: [PATCH 2/2] Default to corect audio device on Raspberry Pi --- docs/README.pod | 2 +- src/audio/alsa.c | 5 ++++- src/audio/omx.c | 5 ++++- src/config.c | 4 ++-- src/main.c | 4 ++-- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/README.pod b/docs/README.pod index 4f94385..16e0a5a 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -137,7 +137,7 @@ To use a different gamepad mapping then the default the B<-mapping> should be sp =item B<-audio> [I] Use as audio output device. -The default value is 'sysdefault' +The default value is 'sysdefault' for ALSA and 'hdmi' for OMX on the Raspberry Pi. =back diff --git a/src/audio/alsa.c b/src/audio/alsa.c index 2815375..273ce82 100644 --- a/src/audio/alsa.c +++ b/src/audio/alsa.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015, 2016 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 @@ -62,6 +62,9 @@ static void alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGU snd_pcm_uframes_t buffer_size = 12 * period_size; unsigned int sampleRate = opusConfig->sampleRate; + if (audio_device == NULL) + audio_device = "sysdefault"; + /* Open PCM device for playback. */ CHECK_RETURN(snd_pcm_open(&handle, audio_device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) diff --git a/src/audio/omx.c b/src/audio/omx.c index b5ae4bd..bf8f7d3 100644 --- a/src/audio/omx.c +++ b/src/audio/omx.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015, 2016 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 @@ -131,6 +131,9 @@ static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR } OMX_CONFIG_BRCMAUDIODESTINATIONTYPE arDest; + if (audio_device == NULL) + audio_device = "hdmi"; + if (audio_device && strlen(audio_device) < sizeof(arDest.sName)) { memset(&arDest, 0, sizeof(OMX_CONFIG_BRCMAUDIODESTINATIONTYPE)); arDest.nSize = sizeof(OMX_CONFIG_BRCMAUDIODESTINATIONTYPE); diff --git a/src/config.c b/src/config.c index fa65fd8..2f660cb 100644 --- a/src/config.c +++ b/src/config.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015, 2016 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 @@ -38,7 +38,7 @@ bool inputAdded = false; static bool mapped = true; -const char* audio_device = "sysdefault"; +const char* audio_device = NULL; static struct option long_options[] = { {"720", no_argument, NULL, 'a'}, diff --git a/src/main.c b/src/main.c index 0314496..e1a5213 100644 --- a/src/main.c +++ b/src/main.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015, 2016 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 @@ -152,7 +152,7 @@ static void help() { printf("\n I/O options\n\n"); printf("\t-mapping \t\tUse as gamepad mapping configuration file (use before -input)\n"); printf("\t-input \t\tUse as input. Can be used multiple times\n"); - printf("\t-audio \t\tUse as audio output device (default sysdefault)\n"); + printf("\t-audio \t\tUse as audio output device\n"); printf("\t-forcehw \t\tTry to use video hardware acceleration\n"); #endif printf("\nUse Ctrl+Alt+Shift+Q to exit streaming session\n\n");