diff options
author | Thomas Reufer <thomas@reufer.ch> | 2014-01-07 16:31:12 +0100 |
---|---|---|
committer | Thomas Reufer <thomas@reufer.ch> | 2014-01-07 16:31:12 +0100 |
commit | 9512123c95324f1679d748993662bd9f08f6f763 (patch) | |
tree | d532e2af4d30847eeaaf69faf427c6f77fc5afb8 | |
parent | 66cb725c2146b4fdeeed1dd201dd58be42104bab (diff) | |
download | vdr-plugin-rpihddevice-0.0.4.tar.gz vdr-plugin-rpihddevice-0.0.4.tar.bz2 |
2013-10-14: Version 0.0.40.0.4
-------------------------
- new:
- changed to libav for audio decoding
- added support multi-channel audio codecs
- added audio format/output options
- fixed:
- removed drawing of black box in front of console which lead to malfunction
due to memory bandwidth problem. console blank out will be handled with
video format/output options in future versions.
- missing
- trick modes
- deinterlacer
- video format/output options
- much more...
-rw-r--r-- | HISTORY | 16 | ||||
-rw-r--r-- | Makefile | 8 | ||||
-rw-r--r-- | audio.c | 229 | ||||
-rw-r--r-- | audio.h | 85 | ||||
-rw-r--r-- | omxdevice.c | 434 | ||||
-rw-r--r-- | omxdevice.h | 32 | ||||
-rw-r--r-- | ovgosd.c | 6 | ||||
-rw-r--r-- | po/de_DE.po | 30 | ||||
-rw-r--r-- | rpihddevice.c | 36 | ||||
-rw-r--r-- | setup.c | 170 | ||||
-rw-r--r-- | setup.h | 57 |
11 files changed, 932 insertions, 171 deletions
@@ -1,6 +1,22 @@ VDR Plugin 'rpihddevice' Revision History ----------------------------------------- +2013-10-14: Version 0.0.4 +------------------------- +- new: + - changed to libav for audio decoding + - added support multi-channel audio codecs + - added audio format/output options +- fixed: + - removed drawing of black box in front of console which lead to malfunction + due to memory bandwidth problem. console blank out will be handled with + video format/output options in future versions. +- missing + - trick modes + - deinterlacer + - video format/output options + - much more... + 2013-10-02: Version 0.0.3 ------------------------- - new: @@ -51,19 +51,21 @@ DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' DEFINES += -DHAVE_LIBOPENMAX=2 -DOMX -DOMX_SKIP64BIT -DUSE_EXTERNAL_OMX -DHAVE_LIBBCM_HOST -DUSE_EXTERNAL_LIBBCM_HOST -DUSE_VCHIQ_ARM DEFINES += -Wno-write-strings -fpermissive +CXXFLAGS += -D__STDC_CONSTANT_MACROS + ILCDIR =ilclient VCINCDIR =/opt/vc/include VCLIBDIR =/opt/vc/lib INCLUDES += -I$(ILCDIR) -I$(VCINCDIR) -I$(VCINCDIR)/interface/vcos/pthreads -I$(VCINCDIR)/interface/vmcs_host/linux -LDFLAGS += -L$(VCLIBDIR) -lbcm_host -lvcos -lvchiq_arm -lopenmaxil -lGLESv2 -lEGL -lpthread -lrt -lmpg123 +LDFLAGS += -L$(VCLIBDIR) -lbcm_host -lvcos -lvchiq_arm -lopenmaxil -lGLESv2 -lEGL -lpthread -lrt -lavcodec -lavformat LDFLAGS += -Wl,--whole-archive $(ILCDIR)/libilclient.a -Wl,--no-whole-archive - + ### The object files (add further files here): ILCLIENT = $(ILCDIR)/libilclient.a -OBJS = $(PLUGIN).o omxdevice.o ovgosd.o +OBJS = $(PLUGIN).o setup.o audio.o omxdevice.o ovgosd.o ### The main target: @@ -0,0 +1,229 @@ +/* + * See the README file for copyright information and how to reach the author. + * + * $Id$ + */ + +#include "audio.h" +#include "setup.h" + +#include <vdr/tools.h> +#include <vdr/remux.h> + +#include <string.h> + +cAudioDecoder::cAudioDecoder() : + m_codec(ePCM), + m_outputFormat(ePCM), + m_outputPort(eLocal), + m_channels(0), + m_samplingRate(0), + m_passthrough(false), + m_frame(0), + m_mutex(new cMutex()) { } + +cAudioDecoder::~cAudioDecoder() +{ + delete m_mutex; +} + +int cAudioDecoder::Init(void) +{ + int ret = 0; + + avcodec_register_all(); + + m_codecs[ePCM ].codec = NULL; + m_codecs[eMPG ].codec = avcodec_find_decoder(CODEC_ID_MP3); + m_codecs[eAC3 ].codec = avcodec_find_decoder(CODEC_ID_AC3); + m_codecs[eEAC3].codec = avcodec_find_decoder(CODEC_ID_EAC3); + m_codecs[eAAC ].codec = avcodec_find_decoder(CODEC_ID_AAC_LATM); + m_codecs[eDTS ].codec = avcodec_find_decoder(CODEC_ID_DTS); + + for (int i = 0; i < eNumCodecs; i++) + { + eCodec codec = static_cast<eCodec>(i); + if (m_codecs[codec].codec) + { + m_codecs[codec].context = avcodec_alloc_context3(m_codecs[codec].codec); + if (!m_codecs[codec].context) + { + esyslog("rpihddevice: failed to allocate %s context!", CodecStr(codec)); + ret = -1; + break; + } + if (avcodec_open2(m_codecs[codec].context, m_codecs[codec].codec, NULL) < 0) + { + esyslog("rpihddevice: failed to open %s decoder!", CodecStr(codec)); + ret = -1; + break; + } + } + } + + m_frame = avcodec_alloc_frame(); + if (!m_frame) + { + esyslog("rpihddevice: failed to allocate audio frame!"); + ret = -1; + } + + if (ret < 0) + DeInit(); + + return ret; +} + +int cAudioDecoder::DeInit(void) +{ + for (int i = 0; i < eNumCodecs; i++) + { + eCodec codec = static_cast<eCodec>(i); + avcodec_close(m_codecs[codec].context); + av_free(m_codecs[codec].context); + } + + av_free(m_frame); + return 0; +} + +bool cAudioDecoder::SetupAudioCodec(const unsigned char *data, int length) +{ + m_mutex->Lock(); + + bool ret = false; + + // try to decode audio sample + AVPacket avpkt; + av_init_packet(&avpkt); + avpkt.data = (unsigned char *)(data + PesPayloadOffset(data)); + avpkt.size = PesLength(data) - PesPayloadOffset(data); + + for (int i = 0; i < eNumCodecs; i++) + { + eCodec codec = static_cast<eCodec>(i); + if (m_codecs[codec].codec) + { + int frame = 0; + avcodec_get_frame_defaults(m_frame); + + m_codecs[codec].context->request_channel_layout = AV_CH_LAYOUT_NATIVE; + m_codecs[codec].context->request_channels = 0; + + int len = avcodec_decode_audio4(m_codecs[codec].context, m_frame, &frame, &avpkt); + + if (len > 0 && frame) + { + m_codec = codec; + m_channels = m_codecs[m_codec].context->channels; + m_samplingRate = m_codecs[m_codec].context->sample_rate; + dsyslog("rpihddevice: set audio codec to %s with %d channels, %dHz", + CodecStr(m_codec), m_channels, m_samplingRate); + + m_passthrough = false; + m_outputFormat = ePCM; + m_outputPort = eLocal; + + if (cRpiSetup::GetAudioPort() == eHDMI && + cRpiSetup::IsAudioFormatSupported(ePCM, 2, 48000)) + { + m_outputPort = eHDMI; + + if (cRpiSetup::IsAudioPassthrough() && + cRpiSetup::IsAudioFormatSupported(m_codec, m_channels, m_samplingRate)) + { + m_passthrough = true; + m_outputFormat = m_codec; + } + dsyslog("rpihddevice: set HDMI audio output format to %s%s", + CodecStr(m_outputFormat), m_passthrough ? " (pass-through)" : ""); + } + else + { + m_codecs[m_codec].context->request_channel_layout = AV_CH_LAYOUT_STEREO_DOWNMIX; + m_codecs[m_codec].context->request_channels = 2; + m_channels = 2; + dsyslog("rpihddevice: set analog audio output format to PCM stereo"); + } + + ret = true; + break; + } + } + } + m_mutex->Unlock(); + return ret; +} + +unsigned int cAudioDecoder::DecodeAudio(const unsigned char *data, int length, unsigned char *outbuf, int bufsize) +{ + m_mutex->Lock(); + + if (m_passthrough) + { + if (length > bufsize) + esyslog("rpihddevice: pass-through audio frame is bigger than output buffer!"); + else + memcpy(outbuf, data, length); + + return length; + } + + AVPacket avpkt; + av_init_packet(&avpkt); + + avpkt.data = (unsigned char *)data; + avpkt.size = length; + + unsigned int outsize = 0; + + while (avpkt.size > 0) + { + int frame = 0; + avcodec_get_frame_defaults(m_frame); + + int len = avcodec_decode_audio4(m_codecs[m_codec].context, m_frame, &frame, &avpkt); + if (len < 0) + break; + + if (frame) + { + unsigned int framesize = av_samples_get_buffer_size(NULL, + m_channels == 6 ? 8 : m_channels, m_frame->nb_samples, + m_codecs[m_codec].context->sample_fmt, 1); + + if (outsize + framesize <= bufsize) + { + if (m_channels == 6) + { + // interleaved copy to fit 5.1 data into 8 channels + int32_t* src = (int32_t*)m_frame->data[0]; + int32_t* dst = (int32_t*)outbuf; + + for (int i = 0; i < m_frame->nb_samples; i++) + { + *dst++ = *src++; // LF & RF + *dst++ = *src++; // CF & LFE + *dst++ = *src++; // LR & RR + *dst++ = 0; // empty channels + } + } + else + memcpy(outbuf, m_frame->data[0], framesize); + + outsize += framesize; + outbuf += framesize; + } + else + { + esyslog("rpihddevice: decoded audio frame is bigger than output buffer!"); + break; + } + } + avpkt.size -= len; + avpkt.data += len; + } + + m_mutex->Unlock(); + return outsize; +} @@ -0,0 +1,85 @@ +/* + * See the README file for copyright information and how to reach the author. + * + * $Id$ + */ + +#ifndef AUDIO_H +#define AUDIO_H + +extern "C" +{ +#include <libavcodec/avcodec.h> +} + +class cMutex; + +class cAudioDecoder +{ + +public: + + enum eCodec { + ePCM, + eMPG, + eAC3, + eEAC3, + eAAC, + eDTS, + eNumCodecs + }; + + enum ePort { + eLocal, + eHDMI + }; + + static const char* CodecStr(eCodec codec) + { + return (codec == ePCM) ? "PCM" : + (codec == eMPG) ? "MPG" : + (codec == eAC3) ? "AC3" : + (codec == eEAC3) ? "E-AC3" : + (codec == eAAC) ? "AAC" : + (codec == eDTS) ? "DTS" : "unknown"; + } + + cAudioDecoder(); + virtual ~cAudioDecoder(); + + virtual int Init(void); + virtual int DeInit(void); + + virtual bool SetupAudioCodec(const unsigned char *data, int length); + + virtual eCodec GetCodec(void) { return m_codec; } + virtual eCodec GetOutputFormat(void) { return m_outputFormat; } + virtual ePort GetOutputPort(void) { return m_outputPort; } + virtual int GetChannels(void) { return m_channels; } + virtual int GetSamplingrate(void) { return m_samplingRate; } + + virtual unsigned int DecodeAudio(const unsigned char *data, int length, + unsigned char *outbuf, int bufsize); + +private: + + struct Codec + { + AVCodec *codec; + AVCodecContext *context; + }; + + Codec m_codecs[eNumCodecs]; + eCodec m_codec; + eCodec m_outputFormat; + ePort m_outputPort; + int m_channels; + int m_samplingRate; + + bool m_passthrough; + + AVFrame *m_frame; + cMutex *m_mutex; +}; + +#endif diff --git a/omxdevice.c b/omxdevice.c index a6514bc..aa5610e 100644 --- a/omxdevice.c +++ b/omxdevice.c @@ -3,15 +3,14 @@ * * $Id$ */ -//#pragma pack(1) #include "omxdevice.h" +#include "audio.h" +#include "setup.h" #include <vdr/remux.h> #include <vdr/tools.h> -#include <mpg123.h> - #include <string.h> extern "C" @@ -21,6 +20,39 @@ extern "C" #include "bcm_host.h" +#define OMX_INIT_STRUCT(a) \ + memset(&(a), 0, sizeof(a)); \ + (a).nSize = sizeof(a); \ + (a).nVersion.s.nVersionMajor = OMX_VERSION_MAJOR; \ + (a).nVersion.s.nVersionMinor = OMX_VERSION_MINOR; \ + (a).nVersion.s.nRevision = OMX_VERSION_REVISION; \ + (a).nVersion.s.nStep = OMX_VERSION_STEP + +#define OMX_AUDIO_CHANNEL_MAPPING(s, c) \ +switch (c) { \ +case 4: \ + (s).eChannelMapping[0] = OMX_AUDIO_ChannelLF; \ + (s).eChannelMapping[1] = OMX_AUDIO_ChannelRF; \ + (s).eChannelMapping[2] = OMX_AUDIO_ChannelLR; \ + (s).eChannelMapping[3] = OMX_AUDIO_ChannelRR; \ + break; \ +case 1: \ + (s).eChannelMapping[0] = OMX_AUDIO_ChannelCF; \ + break; \ +case 8: \ + (s).eChannelMapping[6] = OMX_AUDIO_ChannelLS; \ + (s).eChannelMapping[7] = OMX_AUDIO_ChannelRS; \ +case 6: \ + (s).eChannelMapping[2] = OMX_AUDIO_ChannelCF; \ + (s).eChannelMapping[3] = OMX_AUDIO_ChannelLFE; \ + (s).eChannelMapping[4] = OMX_AUDIO_ChannelLR; \ + (s).eChannelMapping[5] = OMX_AUDIO_ChannelRR; \ +case 2: \ +default: \ + (s).eChannelMapping[0] = OMX_AUDIO_ChannelLF; \ + (s).eChannelMapping[1] = OMX_AUDIO_ChannelRF; \ + break; } + class cOmx { @@ -126,11 +158,31 @@ private: void HandlePortSettingsChanged(unsigned int portId) { - dsyslog("rpihddevice: HandlePortSettingsChanged(%d)", portId); + //dsyslog("rpihddevice: HandlePortSettingsChanged(%d)", portId); switch (portId) { case 131: + + OMX_PARAM_PORTDEFINITIONTYPE portdef; + OMX_INIT_STRUCT(portdef); + portdef.nPortIndex = 131; + if (OMX_GetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]), OMX_IndexParamPortDefinition, + &portdef) != OMX_ErrorNone) + esyslog("rpihddevice: failed to get video decoder port format!"); + + OMX_CONFIG_INTERLACETYPE interlace; + OMX_INIT_STRUCT(interlace); + interlace.nPortIndex = 131; + if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eVideoDecoder]), OMX_IndexConfigCommonInterlace, + &interlace) != OMX_ErrorNone) + esyslog("rpihddevice: failed to get video decoder interlace config!"); + + dsyslog("rpihddevice: decoding video %dx%d%s", + portdef.format.video.nFrameWidth, + portdef.format.video.nFrameHeight, + interlace.eMode == OMX_InterlaceProgressive ? "p" : "i"); + if (ilclient_setup_tunnel(&m_tun[eVideoDecoderToVideoScheduler], 0, 0) != 0) esyslog("rpihddevice: failed to setup up tunnel from video decoder to scheduler!"); if (ilclient_change_component_state(m_comp[eVideoScheduler], OMX_StateExecuting) != 0) @@ -256,9 +308,7 @@ public: esyslog("rpihddevice: failed to setup up tunnel from clock to audio render!"); OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE refclock; - memset(&refclock, 0, sizeof(refclock)); - refclock.nSize = sizeof(refclock); - refclock.nVersion.nVersion = OMX_VERSION; + OMX_INIT_STRUCT(refclock); refclock.eClock = OMX_TIME_RefClockAudio; // refclock.eClock = OMX_TIME_RefClockVideo; @@ -270,24 +320,20 @@ public: ilclient_change_component_state(m_comp[eVideoDecoder], OMX_StateIdle); // set up the number and size of buffers for audio render - m_freeAudioBuffers = 64; + m_freeAudioBuffers = 16; OMX_PARAM_PORTDEFINITIONTYPE param; - memset(¶m, 0, sizeof(param)); - param.nSize = sizeof(param); - param.nVersion.nVersion = OMX_VERSION; + OMX_INIT_STRUCT(param); param.nPortIndex = 100; if (OMX_GetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), OMX_IndexParamPortDefinition, ¶m) != OMX_ErrorNone) esyslog("rpihddevice: failed to get audio render port parameters!"); - param.nBufferSize = 65536; + param.nBufferSize = 128 * 1024; param.nBufferCountActual = m_freeAudioBuffers; if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), OMX_IndexParamPortDefinition, ¶m) != OMX_ErrorNone) esyslog("rpihddevice: failed to set audio render port parameters!"); - memset(¶m, 0, sizeof(param)); - param.nSize = sizeof(param); - param.nVersion.nVersion = OMX_VERSION; + OMX_INIT_STRUCT(param); param.nPortIndex = 130; if (OMX_GetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]), OMX_IndexParamPortDefinition, ¶m) != OMX_ErrorNone) @@ -297,41 +343,9 @@ public: dsyslog("rpihddevice: started with %d video and %d audio buffers", m_freeVideoBuffers, m_freeAudioBuffers); - // configure audio render - OMX_AUDIO_PARAM_PCMMODETYPE pcmMode; - memset(&pcmMode, 0, sizeof(pcmMode)); - pcmMode.nSize = sizeof(pcmMode); - pcmMode.nVersion.nVersion = OMX_VERSION; - pcmMode.nPortIndex = 100; - pcmMode.nChannels = 2; - pcmMode.eNumData = OMX_NumericalDataSigned; - pcmMode.eEndian = OMX_EndianLittle; - pcmMode.bInterleaved = OMX_TRUE; - pcmMode.nBitPerSample = 16; - pcmMode.nSamplingRate = 48000; - pcmMode.ePCMMode = OMX_AUDIO_PCMModeLinear; - pcmMode.eChannelMapping[0] = OMX_AUDIO_ChannelLF; - pcmMode.eChannelMapping[1] = OMX_AUDIO_ChannelRF; - - if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), - OMX_IndexParamAudioPcm, &pcmMode) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set audio render parameters!"); - - OMX_CONFIG_BRCMAUDIODESTINATIONTYPE audioDest; - memset(&audioDest, 0, sizeof(audioDest)); - audioDest.nSize = sizeof(audioDest); - audioDest.nVersion.nVersion = OMX_VERSION; - strcpy((char *)audioDest.sName, "local"); - - if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eAudioRender]), - OMX_IndexConfigBrcmAudioDestination, &audioDest) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set audio destination!"); - /* // configure video decoder stall callback OMX_CONFIG_BUFFERSTALLTYPE stallConf; - memset(&stallConf, 0, sizeof(stallConf)); - stallConf.nSize = sizeof(stallConf); - stallConf.nVersion.nVersion = OMX_VERSION; + OMX_INIT_STRUCT(stallConf); stallConf.nPortIndex = 131; stallConf.nDelay = 1500 * 1000; if (OMX_SetConfig(m_comp[eVideoDecoder], OMX_IndexConfigBufferStall, @@ -339,9 +353,7 @@ public: esyslog("rpihddevice: failed to set video decoder stall config!"); OMX_CONFIG_REQUESTCALLBACKTYPE reqCallback; - memset(&reqCallback, 0, sizeof(reqCallback)); - reqCallback.nSize = sizeof(reqCallback); - reqCallback.nVersion.nVersion = OMX_VERSION; + OMX_INIT_STRUCT(reqCallback); reqCallback.nPortIndex = 131; reqCallback.nIndex = OMX_IndexConfigBufferStall; reqCallback.bEnable = OMX_TRUE; @@ -390,10 +402,8 @@ public: int64_t stc = -1; OMX_TIME_CONFIG_TIMESTAMPTYPE timestamp; - memset(×tamp, 0, sizeof(timestamp)); - timestamp.nSize = sizeof(timestamp); + OMX_INIT_STRUCT(timestamp); timestamp.nPortIndex = OMX_ALL; - timestamp.nVersion.nVersion = OMX_VERSION; if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eClock]), OMX_IndexConfigTimeCurrentMediaTime, ×tamp) != OMX_ErrorNone) @@ -407,9 +417,7 @@ public: bool IsClockRunning(void) { OMX_TIME_CONFIG_CLOCKSTATETYPE cstate; - memset(&cstate, 0, sizeof(cstate)); - cstate.nSize = sizeof(cstate); - cstate.nVersion.nVersion = OMX_VERSION; + OMX_INIT_STRUCT(cstate); if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eClock]), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) @@ -439,9 +447,7 @@ public: clockState == eClockStateWaitForAudioVideo ? "eClockStateWaitForAudioVideo" : "unknown"); OMX_TIME_CONFIG_CLOCKSTATETYPE cstate; - memset(&cstate, 0, sizeof(cstate)); - cstate.nSize = sizeof(cstate); - cstate.nVersion.nVersion = OMX_VERSION; + OMX_INIT_STRUCT(cstate); if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eClock]), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) @@ -497,9 +503,7 @@ public: void SetClockScale(float scale) { OMX_TIME_CONFIG_SCALETYPE scaleType; - memset(&scaleType, 0, sizeof(scaleType)); - scaleType.nSize = sizeof(scaleType); - scaleType.nVersion.nVersion = OMX_VERSION; + OMX_INIT_STRUCT(scaleType); scaleType.xScale = floor(scale * pow(2, 16)); if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]), OMX_IndexConfigTimeScale, &scaleType) != OMX_ErrorNone) @@ -513,9 +517,7 @@ public: dsyslog("rpihddevice: SetVolumeDevice(%d)", vol); OMX_AUDIO_CONFIG_VOLUMETYPE volume; - memset(&volume, 0, sizeof(volume)); - volume.nSize = sizeof(volume); - volume.nVersion.nVersion = OMX_VERSION; + OMX_INIT_STRUCT(volume); volume.nPortIndex = 100; volume.bLinear = OMX_TRUE; volume.sVolume.nValue = vol * 100 / 255; @@ -572,23 +574,23 @@ public: SetClockState(eClockStateStop); } - int SetVideoCodec(OMX_VIDEO_CODINGTYPE coding) + int SetVideoCodec(cOmxDevice::eVideoCodec codec) { // configure video decoder OMX_VIDEO_PARAM_PORTFORMATTYPE videoFormat; - memset(&videoFormat, 0, sizeof(videoFormat)); - videoFormat.nSize = sizeof(videoFormat); - videoFormat.nVersion.nVersion = OMX_VERSION; + OMX_INIT_STRUCT(videoFormat); videoFormat.nPortIndex = 130; - videoFormat.eCompressionFormat = coding; + videoFormat.eCompressionFormat = + codec == cOmxDevice::eMPEG2 ? OMX_VIDEO_CodingMPEG2 : + codec == cOmxDevice::eH264 ? OMX_VIDEO_CodingAVC : + OMX_VIDEO_CodingAutoDetect; + if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]), OMX_IndexParamVideoPortFormat, &videoFormat) != OMX_ErrorNone) esyslog("rpihddevice: failed to set video decoder parameters!"); OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE ectype; - memset (&ectype, 0, sizeof(ectype)); - ectype.nSize = sizeof(ectype); - ectype.nVersion.nVersion = OMX_VERSION; + OMX_INIT_STRUCT(ectype); ectype.bStartWithValidFrame = OMX_FALSE; if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]), OMX_IndexParamBrcmVideoDecodeErrorConcealment, &ectype) != OMX_ErrorNone) @@ -607,8 +609,125 @@ public: return 0; } - int SetAudioCodec(OMX_AUDIO_CODINGTYPE coding) + int SetupAudioRender(cAudioDecoder::eCodec outputFormat, int channels, int samplingRate, + cAudioDecoder::ePort audioPort) { + // put audio render onto idle + ilclient_flush_tunnels(&m_tun[eClockToAudioRender], 1); + ilclient_disable_tunnel(&m_tun[eClockToAudioRender]); + ilclient_change_component_state(m_comp[eAudioRender], OMX_StateIdle); + ilclient_disable_port_buffers(m_comp[eAudioRender], 100, NULL, NULL, NULL); + + OMX_AUDIO_PARAM_PORTFORMATTYPE format; + OMX_INIT_STRUCT(format); + format.nPortIndex = 100; + if (OMX_GetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), + OMX_IndexParamAudioPortFormat, &format) != OMX_ErrorNone) + esyslog("rpihddevice: failed to get audio port format parameters!"); + + format.eEncoding = + outputFormat == cAudioDecoder::ePCM ? OMX_AUDIO_CodingPCM : + outputFormat == cAudioDecoder::eMPG ? OMX_AUDIO_CodingMP3 : + outputFormat == cAudioDecoder::eAC3 ? OMX_AUDIO_CodingDDP : + outputFormat == cAudioDecoder::eEAC3 ? OMX_AUDIO_CodingDDP : + outputFormat == cAudioDecoder::eAAC ? OMX_AUDIO_CodingAAC : + outputFormat == cAudioDecoder::eDTS ? OMX_AUDIO_CodingDTS : + OMX_AUDIO_CodingAutoDetect; + + if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), + OMX_IndexParamAudioPortFormat, &format) != OMX_ErrorNone) + esyslog("rpihddevice: failed to set audio port format parameters!"); + + switch (outputFormat) + { + case cAudioDecoder::eMPG: + OMX_AUDIO_PARAM_MP3TYPE mp3; + OMX_INIT_STRUCT(mp3); + mp3.nPortIndex = 100; + mp3.nChannels = channels; + mp3.nSampleRate = samplingRate; + mp3.eChannelMode = OMX_AUDIO_ChannelModeStereo; // ? + mp3.eFormat = OMX_AUDIO_MP3StreamFormatMP1Layer3; // should be MPEG-1 layer 2 + + if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), + OMX_IndexParamAudioMp3, &mp3) != OMX_ErrorNone) + esyslog("rpihddevice: failed to set audio render mp3 parameters!"); + break; + + case cAudioDecoder::eAC3: + case cAudioDecoder::eEAC3: + OMX_AUDIO_PARAM_DDPTYPE ddp; + OMX_INIT_STRUCT(ddp); + ddp.nPortIndex = 100; + ddp.nChannels = channels; + ddp.nSampleRate = samplingRate; + OMX_AUDIO_CHANNEL_MAPPING(ddp, channels); + + if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), + OMX_IndexParamAudioDdp, &ddp) != OMX_ErrorNone) + esyslog("rpihddevice: failed to set audio render ddp parameters!"); + break; + + case cAudioDecoder::eAAC: + OMX_AUDIO_PARAM_AACPROFILETYPE aac; + OMX_INIT_STRUCT(aac); + aac.nPortIndex = 100; + aac.nChannels = channels; + aac.nSampleRate = samplingRate; + aac.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4LATM; + + if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), + OMX_IndexParamAudioAac, &aac) != OMX_ErrorNone) + esyslog("rpihddevice: failed to set audio render aac parameters!"); + break; + + case cAudioDecoder::eDTS: + OMX_AUDIO_PARAM_DTSTYPE dts; + OMX_INIT_STRUCT(aac); + dts.nPortIndex = 100; + dts.nChannels = channels; + dts.nSampleRate = samplingRate; + dts.nDtsType = 1; // ?? + dts.nFormat = 0; // ?? + dts.nDtsFrameSizeBytes = 0; // ? + OMX_AUDIO_CHANNEL_MAPPING(dts, channels); + + if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), + OMX_IndexParamAudioDts, &dts) != OMX_ErrorNone) + esyslog("rpihddevice: failed to set audio render dts parameters!"); + break; + + case cAudioDecoder::ePCM: + OMX_AUDIO_PARAM_PCMMODETYPE pcm; + OMX_INIT_STRUCT(pcm); + pcm.nPortIndex = 100; + pcm.nChannels = channels == 6 ? 8 : channels; + pcm.eNumData = OMX_NumericalDataSigned; + pcm.eEndian = OMX_EndianLittle; + pcm.bInterleaved = OMX_TRUE; + pcm.nBitPerSample = 16; + pcm.nSamplingRate = samplingRate; + pcm.ePCMMode = OMX_AUDIO_PCMModeLinear; + OMX_AUDIO_CHANNEL_MAPPING(pcm, channels); + + if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), + OMX_IndexParamAudioPcm, &pcm) != OMX_ErrorNone) + esyslog("rpihddevice: failed to set audio render pcm parameters!"); + break; + + default: + break; + } + + OMX_CONFIG_BRCMAUDIODESTINATIONTYPE audioDest; + OMX_INIT_STRUCT(audioDest); + strcpy((char *)audioDest.sName, + audioPort == cAudioDecoder::eLocal ? "local" : "hdmi"); + + if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eAudioRender]), + OMX_IndexConfigBrcmAudioDestination, &audioDest) != OMX_ErrorNone) + esyslog("rpihddevice: failed to set audio destination!"); + if (ilclient_enable_port_buffers(m_comp[eAudioRender], 100, NULL, NULL, NULL) != 0) esyslog("rpihddevice: failed to enable port buffer on audio render!"); @@ -632,7 +751,7 @@ public: if (buf != NULL) { cOmx::PtsToTicks(pts, buf->nTimeStamp); - buf->nFlags = m_firstAudioBuffer ? OMX_BUFFERFLAG_STARTTIME : OMX_BUFFERFLAG_TIME_UNKNOWN; + buf->nFlags = m_firstAudioBuffer ? OMX_BUFFERFLAG_STARTTIME : 0; //OMX_BUFFERFLAG_TIME_UNKNOWN; m_firstAudioBuffer = false; m_freeAudioBuffers--; } @@ -653,7 +772,7 @@ public: if (buf != NULL) { cOmx::PtsToTicks(pts, buf->nTimeStamp); - buf->nFlags = m_firstVideoBuffer ? OMX_BUFFERFLAG_STARTTIME : OMX_BUFFERFLAG_TIME_UNKNOWN; + buf->nFlags = m_firstVideoBuffer ? OMX_BUFFERFLAG_STARTTIME : 0; //OMX_BUFFERFLAG_TIME_UNKNOWN; m_firstVideoBuffer = false; m_freeVideoBuffers--; } @@ -672,7 +791,7 @@ public: if (!buf) return false; - return (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_comp[eAudioRender]), buf) != OMX_ErrorNone); + return (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_comp[eAudioRender]), buf) == OMX_ErrorNone); } bool EmptyVideoBuffer(OMX_BUFFERHEADERTYPE *buf) @@ -680,12 +799,12 @@ public: if (!buf) return false; - return (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_comp[eVideoDecoder]), buf) != OMX_ErrorNone); + return (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_comp[eVideoDecoder]), buf) == OMX_ErrorNone); } }; /* ------------------------------------------------------------------------- */ - +#if 0 class cAudio { @@ -736,18 +855,19 @@ public: mpg123_handle *m_handle; }; - +#endif /* ------------------------------------------------------------------------- */ cOmxDevice::cOmxDevice(void (*onPrimaryDevice)(void)) : cDevice(), m_onPrimaryDevice(onPrimaryDevice), m_omx(new cOmx()), - m_audio(new cAudio()), + m_audio(new cAudioDecoder()), m_mutex(new cMutex()), m_state(eStop), m_audioCodecReady(false), - m_videoCodecReady(false) + m_videoCodecReady(false), + m_audioId(0) { } @@ -760,12 +880,37 @@ cOmxDevice::~cOmxDevice() int cOmxDevice::Init(void) { - return m_omx->Init(); + if (m_audio->Init() < 0) + { + esyslog("rpihddevice: failed to initialize audio!"); + return -1; + } + if (m_omx->Init() < 0) + { + esyslog("rpihddevice: failed to initialize OMX!"); + return -1; + } + return 0; } int cOmxDevice::DeInit(void) { - return m_omx->DeInit(); + if (m_omx->DeInit() < 0) + { + esyslog("rpihddevice: failed to deinitialize OMX!"); + return -1; + } + if (m_audio->DeInit() < 0) + { + esyslog("rpihddevice: failed to deinitialize audio!"); + return -1; + } + return 0; +} + +void cOmxDevice::GetOsdSize(int &Width, int &Height, double &PixelAspect) +{ + cRpiSetup::GetDisplaySize(Width, Height, PixelAspect); } bool cOmxDevice::CanReplay(void) const @@ -837,43 +982,52 @@ int cOmxDevice::PlayAudio(const uchar *Data, int Length, uchar Id) } int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : 0; - const uchar *payload = Data + PesPayloadOffset(Data); int length = PesLength(Data) - PesPayloadOffset(Data); - // try to init codec if PTS is valid - if (!m_audioCodecReady && pts != 0) - m_audioCodecReady = OmxSetAudioCodec(payload); + if (m_audioId != Id) + { + m_audioId = Id; + m_audioCodecReady = false; + } - if (!m_audioCodecReady) + // try to init codec + if (!m_audioCodecReady || cRpiSetup::HasAudioSetupChanged()) { - m_mutex->Unlock(); - return Length; + if (m_audio->SetupAudioCodec(Data, Length)) + { + m_audioCodecReady = true; + m_omx->SetupAudioRender( + m_audio->GetOutputFormat(), + m_audio->GetChannels(), + m_audio->GetSamplingrate(), + m_audio->GetOutputPort()); + } + else + { + m_mutex->Unlock(); + return Length; + } } - if (!m_audio->writeData(payload, length)) + OMX_BUFFERHEADERTYPE *buf = m_omx->GetAudioBuffer(pts); + if (buf == NULL) { - esyslog("rpihddevice: failed to pass buffer to audio decoder!"); m_mutex->Unlock(); return 0; } - bool done = false; - while (!done) - { - OMX_BUFFERHEADERTYPE *buf = m_omx->GetAudioBuffer(pts); - if (buf == NULL) - { - //esyslog("rpihddevice: failed to get audio buffer!"); - m_mutex->Unlock(); - return 0; - } + // decode and write audio packet + buf->nFilledLen = m_audio->DecodeAudio(payload, length, buf->pBuffer, buf->nAllocLen); - // decode and write audio packet - buf->nFilledLen = m_audio->readSamples(buf->pBuffer, buf->nAllocLen, done); + // if decoding failed, reset audio codec + if (!buf->nFilledLen) + m_audioCodecReady = false; - if (m_omx->EmptyAudioBuffer(buf)) - break; + if (!m_omx->EmptyAudioBuffer(buf)) + { + m_mutex->Unlock(); + return 0; } m_mutex->Unlock(); @@ -910,7 +1064,15 @@ int cOmxDevice::PlayVideo(const uchar *Data, int Length) // try to init codec if PTS is valid if (!m_videoCodecReady && pts != 0) - m_videoCodecReady = OmxSetVideoCodec(payload); + { + eVideoCodec codec = GetVideoCodec(Data, Length); + if (cRpiSetup::IsVideoCodecSupported(codec)) + { + m_videoCodecReady = (m_omx->SetVideoCodec(codec) == 0); + dsyslog("rpihddevice: set video codec to %s!", + VideoCodecStr(codec)); + } + } if (!m_videoCodecReady) { @@ -937,7 +1099,7 @@ int cOmxDevice::PlayVideo(const uchar *Data, int Length) // dsyslog("rpihddevice: PlayVideo(%u.%u, %02x %02x %02x %02x %02x %02x %02x %02x, %d)", buf->nTimeStamp.nHighPart, buf->nTimeStamp.nLowPart, // buf->pBuffer[0], buf->pBuffer[1], buf->pBuffer[2], buf->pBuffer[3], // buf->pBuffer[4], buf->pBuffer[5], buf->pBuffer[6], buf->pBuffer[7], buf->nFilledLen); - if (m_omx->EmptyVideoBuffer(buf)) + if (!m_omx->EmptyVideoBuffer(buf)) esyslog("rpihddevice: failed to pass buffer to video decoder!"); m_mutex->Unlock(); @@ -953,12 +1115,14 @@ void cOmxDevice::Play(void) { dsyslog("rpihddevice: Play()"); m_omx->SetClockScale(1.0f); + cDevice::Play(); } void cOmxDevice::Freeze(void) { dsyslog("rpihddevice: Freeze()"); m_omx->SetClockScale(0.0f); + cDevice::Freeze(); } void cOmxDevice::TrickSpeed(int Speed) @@ -1004,46 +1168,26 @@ void cOmxDevice::MakePrimaryDevice(bool On) cDevice::MakePrimaryDevice(On); } -bool cOmxDevice::OmxSetVideoCodec(const uchar *data) +cOmxDevice::eVideoCodec cOmxDevice::GetVideoCodec(const uchar *data, int length) { - if (data[0] != 0x00 || data[1] != 0x00) - return false; + if (PesLength(data) - PesPayloadOffset(data) < 6) + return eUnknown; - if (data[2] == 0x01 && data[3] == 0xb3) - { - dsyslog("rpihddevice: set video codec to MPEG2"); - return (m_omx->SetVideoCodec(OMX_VIDEO_CodingMPEG2) == 0); - } - - else if ((data[2] == 0x01 && data[3] == 0x09 && data[4] == 0x10) || - (data[2] == 0x00 && data[3] == 0x01 && data[4] == 0x09 && data[5] == 0x10)) - { - dsyslog("rpihddevice: set video codec to H264"); - return (m_omx->SetVideoCodec(OMX_VIDEO_CodingAVC) == 0); - } + const uchar *p = data + PesPayloadOffset(data); - esyslog("rpihddevice: invalid start sequence: %02x %02x %02x %02x %02x %02x %02x %02x", - data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); + if (p[0] != 0x00 || p[1] != 0x00) + return eUnknown; - return false; -} + if (p[2] == 0x01 && p[3] == 0xb3) + return eMPEG2; -bool cOmxDevice::OmxSetAudioCodec(const uchar *data) -{ - return (m_omx->SetAudioCodec(OMX_AUDIO_CodingPCM) == 0); -} + else if ((p[2] == 0x01 && p[3] == 0x09 && p[4] == 0x10) || + (p[2] == 0x00 && p[3] == 0x01 && p[4] == 0x09 && p[5] == 0x10)) + return eH264; -void cOmxDevice::GetDisplaySize(int &width, int &height, double &aspect) -{ - uint32_t screenWidth; - uint32_t screenHeight; + //esyslog("rpihddevice: invalid start sequence: " + // "%02x %02x %02x %02x %02x %02x %02x %02x", + // p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); - if (graphics_get_display_size(0 /* LCD */, &screenWidth, &screenHeight) < 0) - esyslog("rpihddevice: failed to get display size!"); - else - { - width = (int)screenWidth; - height = (int)screenHeight; - aspect = 1; - } + return eUnknown; } diff --git a/omxdevice.h b/omxdevice.h index ea3628a..affe2a9 100644 --- a/omxdevice.h +++ b/omxdevice.h @@ -11,7 +11,7 @@ #include <vdr/thread.h> class cOmx; -class cAudio; +class cAudioDecoder; class cOmxDevice : cDevice { @@ -24,12 +24,26 @@ public: ePlay }; + enum eVideoCodec { + eMPEG2, + eH264, + eUnknown + }; + + static const char* VideoCodecStr(eVideoCodec codec) + { + return (codec == eMPEG2) ? "MPEG2" : + (codec == eH264) ? "H264" : "unknown"; + } + cOmxDevice(void (*onPrimaryDevice)(void)); virtual ~cOmxDevice(); virtual int Init(void); virtual int DeInit(void); + virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect); + virtual bool HasDecoder(void) const { return true; }; virtual bool SetPlayMode(ePlayMode PlayMode); @@ -52,11 +66,6 @@ public: virtual bool Poll(cPoller &Poller, int TimeoutMs = 0); - virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect) - { cOmxDevice::GetDisplaySize(Width, Height, PixelAspect); } - - static void GetDisplaySize(int &width, int &height, double &aspect); - protected: virtual void MakePrimaryDevice(bool On); @@ -65,17 +74,18 @@ private: void (*m_onPrimaryDevice)(void); - virtual bool OmxSetVideoCodec(const uchar *data); - virtual bool OmxSetAudioCodec(const uchar *data); + virtual eVideoCodec GetVideoCodec(const uchar *data, int length); - cOmx *m_omx; - cAudio *m_audio; - cMutex *m_mutex; + cOmx *m_omx; + cAudioDecoder *m_audio; + cMutex *m_mutex; eState m_state; bool m_audioCodecReady; bool m_videoCodecReady; + + uchar m_audioId; }; #endif @@ -11,6 +11,7 @@ #include "ovgosd.h" #include "omxdevice.h" +#include "setup.h" class cOvg : public cThread { @@ -32,7 +33,7 @@ public: m_h(0), m_clear(false) { - cOmxDevice::GetDisplaySize(m_width, m_height, m_aspect); + cRpiSetup::GetDisplaySize(m_width, m_height, m_aspect); m_do = new cCondWait(); m_done = new cCondWait(); @@ -128,6 +129,7 @@ protected: dispmanUpdate, dispmanDisplay, 2 /*layer*/, &dstRect, 0, &srcRect, DISPMANX_PROTECTION_NONE, 0, 0, (DISPMANX_TRANSFORM_T)0); +#if 0 // create black layer in front of console uint32_t pBgImage; uint16_t bgImage = 0x0000; // black @@ -138,7 +140,7 @@ protected: vc_dispmanx_rect_set(&dstRect, 0, 0, 1 << 16, 1 << 16); vc_dispmanx_element_add(dispmanUpdate, dispmanDisplay, -1 /*layer*/, &srcRect, bgRsc, &dstRect, DISPMANX_PROTECTION_NONE, 0, 0, (DISPMANX_TRANSFORM_T)0); - +#endif vc_dispmanx_update_submit_sync(dispmanUpdate); EGL_DISPMANX_WINDOW_T nativewindow; diff --git a/po/de_DE.po b/po/de_DE.po new file mode 100644 index 0000000..7d6bc20 --- /dev/null +++ b/po/de_DE.po @@ -0,0 +1,30 @@ +# German translations for vdr-rpihddevice package. +# Copyright (C) 2013 THE vdr-rpihddevice'S COPYRIGHT HOLDER +# This file is distributed under the same license as the vdr-rpihddevice package. +# <thomas@reufer.ch>, 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: vdr-rpihddevice 0.0.4\n" +"Report-Msgid-Bugs-To: <see README>\n" +"POT-Creation-Date: 2013-10-14 13:33+0200\n" +"PO-Revision-Date: 2013-10-14 13:36+0200\n" +"Last-Translator: <thomas@reufer.ch>\n" +"Language-Team: German <translation-team-de@lists.sourceforge.net>\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ASCII\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "analog" +msgstr "analog" + +msgid "HDMI" +msgstr "HDMI" + +msgid "Audio Port" +msgstr "Audioanschluss" + +msgid "Digital Audio Pass-Through" +msgstr "Digitalton durchreichen" diff --git a/rpihddevice.c b/rpihddevice.c index 9ca329a..fb90c11 100644 --- a/rpihddevice.c +++ b/rpihddevice.c @@ -9,10 +9,9 @@ #include "ovgosd.h" #include "omxdevice.h" +#include "setup.h" -#include "bcm_host.h" - -static const char *VERSION = "0.0.3"; +static const char *VERSION = "0.0.4"; static const char *DESCRIPTION = "HD output device for Raspberry Pi"; class cDummyDevice : cDevice @@ -29,7 +28,8 @@ public: virtual bool Poll(cPoller &Poller, int TimeoutMs = 0) { return true; } virtual bool Flush(int TimeoutMs = 0) { return true; } virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect) - { cOmxDevice::GetDisplaySize(Width, Height, PixelAspect); } + { cRpiSetup::GetDisplaySize(Width, Height, PixelAspect); } + bool Start(void) {return true;} protected: @@ -43,7 +43,7 @@ private: cOmxDevice *m_device; - static void OnPrimaryDevice() { new cRpiOsdProvider(); } + static void OnPrimaryDevice(void) { new cRpiOsdProvider(); } public: cPluginRpiHdDevice(void); @@ -58,30 +58,36 @@ public: virtual void Housekeeping(void) {} virtual const char *MainMenuEntry(void) { return NULL; } virtual cOsdObject *MainMenuAction(void) { return NULL; } - virtual cMenuSetupPage *SetupMenu(void) { return NULL; } - virtual bool SetupParse(const char *Name, const char *Value) { return false; }; + virtual cMenuSetupPage *SetupMenu(void); + virtual bool SetupParse(const char *Name, const char *Value); }; cPluginRpiHdDevice::cPluginRpiHdDevice(void) : m_device(0) { - m_device = new cOmxDevice(&cPluginRpiHdDevice::OnPrimaryDevice); //new cDummyDevice(); } cPluginRpiHdDevice::~cPluginRpiHdDevice() { delete m_device; + cRpiSetup::DropInstance(); } bool cPluginRpiHdDevice::Initialize(void) { - bcm_host_init(); + if (!cRpiSetup::HwInit()) + return false; + + if (!cRpiSetup::IsVideoCodecSupported(cOmxDevice::eMPEG2)) + esyslog("rpihddevice: WARNING: MPEG2 video decoder not enabled!"); + + m_device = new cOmxDevice(&OnPrimaryDevice); if (m_device) return (m_device->Init() == 0); - return true; + return false; } bool cPluginRpiHdDevice::Start(void) @@ -95,4 +101,14 @@ void cPluginRpiHdDevice::Stop(void) m_device->DeInit(); } +cMenuSetupPage* cPluginRpiHdDevice::SetupMenu(void) +{ + return cRpiSetup::GetInstance()->GetSetupPage(); +} + +bool cPluginRpiHdDevice::SetupParse(const char *Name, const char *Value) +{ + return cRpiSetup::GetInstance()->Parse(Name, Value); +} + VDRPLUGINCREATOR(cPluginRpiHdDevice); // Don't touch this! @@ -0,0 +1,170 @@ +/* + * See the README file for copyright information and how to reach the author. + * + * $Id$ + */ + +#include "setup.h" + +#include <vdr/tools.h> +#include <vdr/menuitems.h> + +#include <bcm_host.h> +#include "interface/vchiq_arm/vchiq_if.h" +#include "interface/vmcs_host/vc_tvservice.h" + +cRpiSetup* cRpiSetup::s_instance = 0; + +cRpiSetup* cRpiSetup::GetInstance(void) +{ + if (!s_instance) + s_instance = new cRpiSetup(); + + return s_instance; +} + +void cRpiSetup::DropInstance(void) +{ + delete s_instance; + s_instance = 0; +} + +class cRpiSetupPage : public cMenuSetupPage +{ + +public: + + cRpiSetupPage(int *audioPort, int *passthrough, bool *audioSetupChanged) : + m_audioPort(audioPort), + m_passthrough(passthrough), + m_audioSetupChanged(audioSetupChanged) + { + static const char *const audioport[] = { tr("analog"), tr("HDMI") }; + + m_newAudioPort = *m_audioPort; + m_newPassthrough = *m_passthrough; + + Add(new cMenuEditStraItem(tr("Audio Port"), &m_newAudioPort, 2, audioport)); + Add(new cMenuEditBoolItem(tr("Digital Audio Pass-Through"), &m_newPassthrough)); + } + +protected: + + virtual void Store(void) + { + *m_audioSetupChanged = + (*m_audioPort != m_newAudioPort) || + (*m_passthrough != m_newPassthrough); + + SetupStore("AudioPort", *m_audioPort = m_newAudioPort); + SetupStore("PassThrough", *m_passthrough = m_newPassthrough); + } + +private: + + int m_newAudioPort; + int m_newPassthrough; + + int *m_audioPort; + int *m_passthrough; + + bool *m_audioSetupChanged; + +}; + +bool cRpiSetup::HwInit(void) +{ + bcm_host_init(); + vcos_init(); + + VCHI_INSTANCE_T vchiInstance; + VCHI_CONNECTION_T *vchiConnections; + if (vchi_initialise(&vchiInstance) != VCHIQ_SUCCESS) + { + esyslog("rpihddevice: failed to open vchiq instance!"); + return false; + } + if (vchi_connect(NULL, 0, vchiInstance) != 0) + { + esyslog("rpihddevice: failed to connect to vchi!"); + return false; + } + if (vc_vchi_tv_init(vchiInstance, &vchiConnections, 1) != 0) + { + esyslog("rpihddevice: failed to connect to tvservice!"); + return false; + } + + if (!vc_gencmd_send("codec_enabled MPG2")) + { + char buffer[1024]; + if (!vc_gencmd_read_response(buffer, sizeof(buffer))) + { + if (!strcasecmp(buffer,"MPG2=enabled")) + GetInstance()->m_mpeg2Enabled = true; + } + } + + return true; +} + +bool cRpiSetup::IsAudioFormatSupported(cAudioDecoder::eCodec codec, int channels, int samplingRate) +{ + if (vc_tv_hdmi_audio_supported( + codec == cAudioDecoder::eMPG ? EDID_AudioFormat_eMPEG1 : + codec == cAudioDecoder::eAC3 ? EDID_AudioFormat_eAC3 : + codec == cAudioDecoder::eEAC3 ? EDID_AudioFormat_eEAC3 : + codec == cAudioDecoder::eAAC ? EDID_AudioFormat_eAAC : + codec == cAudioDecoder::eDTS ? EDID_AudioFormat_eDTS : + EDID_AudioFormat_ePCM, channels, + samplingRate == 32000 ? EDID_AudioSampleRate_e32KHz : + samplingRate == 44000 ? EDID_AudioSampleRate_e44KHz : + samplingRate == 88000 ? EDID_AudioSampleRate_e88KHz : + samplingRate == 96000 ? EDID_AudioSampleRate_e96KHz : + samplingRate == 176000 ? EDID_AudioSampleRate_e176KHz : + samplingRate == 192000 ? EDID_AudioSampleRate_e192KHz : + EDID_AudioSampleRate_e48KHz, EDID_AudioSampleSize_16bit) == 0) + return true; + + return false; +} + +int cRpiSetup::GetDisplaySize(int &width, int &height, double &aspect) +{ + uint32_t screenWidth; + uint32_t screenHeight; + + if (graphics_get_display_size(0 /* LCD */, &screenWidth, &screenHeight) < 0) + esyslog("rpihddevice: failed to get display size!"); + else + { + width = (int)screenWidth; + height = (int)screenHeight; + aspect = 1; + return 0; + } + return -1; +} + +bool cRpiSetup::HasAudioSetupChanged(void) +{ + if (!GetInstance()->m_audioSetupChanged) + return false; + + GetInstance()->m_audioSetupChanged = false; + return true; +} + +cMenuSetupPage* cRpiSetup::GetSetupPage(void) +{ + return new cRpiSetupPage(&m_audioPort, &m_passthrough, &m_audioSetupChanged); +} + +bool cRpiSetup::Parse(const char *name, const char *value) +{ + if (!strcasecmp(name, "AudioPort")) m_audioPort = atoi(value); + else if (!strcasecmp(name, "PassThrough")) m_passthrough = atoi(value); + else return false; + + return true; +} @@ -0,0 +1,57 @@ +/* + * See the README file for copyright information and how to reach the author. + * + * $Id$ + */ + +#ifndef SETUP_H +#define SETUP_H + +#include "audio.h" +#include "omxdevice.h" + +class cMenuSetupPage; + +class cRpiSetup +{ + +public: + + static bool HwInit(void); + + static cAudioDecoder::ePort GetAudioPort(void) { + return (GetInstance()->m_audioPort) ? cAudioDecoder::eHDMI : cAudioDecoder::eLocal; } + static bool IsAudioPassthrough(void) { return GetInstance()->m_passthrough; } + static bool HasAudioSetupChanged(void); + + static bool IsAudioFormatSupported(cAudioDecoder::eCodec codec, int channels, int samplingRate); + + static bool IsVideoCodecSupported(cOmxDevice::eVideoCodec codec) { + return codec == cOmxDevice::eMPEG2 ? GetInstance()->m_mpeg2Enabled : + codec == cOmxDevice::eH264 ? true : false; + } + + static int GetDisplaySize(int &width, int &height, double &aspect); + + static cRpiSetup* GetInstance(void); + static void DropInstance(void); + + cMenuSetupPage* GetSetupPage(void); + bool Parse(const char *name, const char *value); + +private: + + cRpiSetup() : m_audioSetupChanged(false), m_mpeg2Enabled(false) { } + virtual ~cRpiSetup() { } + + static cRpiSetup* s_instance; + + int m_audioPort; + int m_passthrough; + + bool m_audioSetupChanged; + + bool m_mpeg2Enabled; +}; + +#endif |