diff options
author | Thomas Reufer <thomas@reufer.ch> | 2014-01-07 16:32:58 +0100 |
---|---|---|
committer | Thomas Reufer <thomas@reufer.ch> | 2014-01-07 16:32:58 +0100 |
commit | dd9fbf38610934623869728d1b00904d15783480 (patch) | |
tree | 789059da0b20f3c7d9711b3dd638a770d2a6cfb2 /omx.c | |
parent | 9512123c95324f1679d748993662bd9f08f6f763 (diff) | |
download | vdr-plugin-rpihddevice-0.0.5.tar.gz vdr-plugin-rpihddevice-0.0.5.tar.bz2 |
2013-11-17: Version 0.0.50.0.5
-------------------------
- new:
- improved audio format detection (taken from softhddevice)
- separate thread for audio decoding
- fixed:
- jump forward/backward in recordings
- several minor bugfixes
- missing:
- still picture
- trick modes
- deinterlacer
- video format/output options
Diffstat (limited to 'omx.c')
-rw-r--r-- | omx.c | 876 |
1 files changed, 876 insertions, 0 deletions
@@ -0,0 +1,876 @@ +/* + * See the README file for copyright information and how to reach the author. + * + * $Id$ + */ + +#include "omx.h" + +#include <vdr/tools.h> +#include <vdr/thread.h> + +extern "C" +{ +#include "ilclient.h" +} + +#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; } + + +const char* cOmx::errStr(int err) +{ + return err == OMX_ErrorNone ? "None" : + err == OMX_ErrorInsufficientResources ? "InsufficientResources" : + err == OMX_ErrorUndefined ? "Undefined" : + err == OMX_ErrorInvalidComponentName ? "InvalidComponentName" : + err == OMX_ErrorComponentNotFound ? "ComponentNotFound" : + err == OMX_ErrorInvalidComponent ? "InvalidComponent" : + err == OMX_ErrorBadParameter ? "BadParameter" : + err == OMX_ErrorNotImplemented ? "NotImplemented" : + err == OMX_ErrorUnderflow ? "Underflow" : + err == OMX_ErrorOverflow ? "Overflow" : + err == OMX_ErrorHardware ? "Hardware" : + err == OMX_ErrorInvalidState ? "InvalidState" : + err == OMX_ErrorStreamCorrupt ? "StreamCorrupt" : + err == OMX_ErrorPortsNotCompatible ? "PortsNotCompatible" : + err == OMX_ErrorResourcesLost ? "ResourcesLost" : + err == OMX_ErrorNoMore ? "NoMore" : + err == OMX_ErrorVersionMismatch ? "VersionMismatch" : + err == OMX_ErrorNotReady ? "NotReady" : + err == OMX_ErrorTimeout ? "Timeout" : + err == OMX_ErrorSameState ? "SameState" : + err == OMX_ErrorResourcesPreempted ? "ResourcesPreempted" : + err == OMX_ErrorPortUnresponsiveDuringAllocation ? "PortUnresponsiveDuringAllocation" : + err == OMX_ErrorPortUnresponsiveDuringDeallocation ? "PortUnresponsiveDuringDeallocation" : + err == OMX_ErrorPortUnresponsiveDuringStop ? "PortUnresponsiveDuringStop" : + err == OMX_ErrorIncorrectStateTransition ? "IncorrectStateTransition" : + err == OMX_ErrorIncorrectStateOperation ? "IncorrectStateOperation" : + err == OMX_ErrorUnsupportedSetting ? "UnsupportedSetting" : + err == OMX_ErrorUnsupportedIndex ? "UnsupportedIndex" : + err == OMX_ErrorBadPortIndex ? "BadPortIndex" : + err == OMX_ErrorPortUnpopulated ? "PortUnpopulated" : + err == OMX_ErrorComponentSuspended ? "ComponentSuspended" : + err == OMX_ErrorDynamicResourcesUnavailable ? "DynamicResourcesUnavailable" : + err == OMX_ErrorMbErrorsInFrame ? "MbErrorsInFrame" : + err == OMX_ErrorFormatNotDetected ? "FormatNotDetected" : + err == OMX_ErrorContentPipeOpenFailed ? "ContentPipeOpenFailed" : + err == OMX_ErrorContentPipeCreationFailed ? "ContentPipeCreationFailed" : + err == OMX_ErrorSeperateTablesUsed ? "SeperateTablesUsed" : + err == OMX_ErrorTunnelingUnsupported ? "TunnelingUnsupported" : + err == OMX_ErrorKhronosExtensions ? "KhronosExtensions" : + err == OMX_ErrorVendorStartUnused ? "VendorStartUnused" : + err == OMX_ErrorDiskFull ? "DiskFull" : + err == OMX_ErrorMaxFileSize ? "MaxFileSize" : + err == OMX_ErrorDrmUnauthorised ? "DrmUnauthorised" : + err == OMX_ErrorDrmExpired ? "DrmExpired" : + err == OMX_ErrorDrmGeneral ? "DrmGeneral" : + "unknown"; +} + +void cOmx::HandleEndOfStream(unsigned int portId) + { + dsyslog("rpihddevice: HandleEndOfStream(%d)", portId); + + switch (portId) + { + case 131: + break; + + case 11: + break; + + case 90: + break; + } +} + +void cOmx::HandlePortSettingsChanged(unsigned int 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) + esyslog("rpihddevice: failed to enable video scheduler!"); + break; + + case 11: + if (ilclient_setup_tunnel(&m_tun[eVideoSchedulerToVideoRender], 0, 1000) != 0) + esyslog("rpihddevice: failed to setup up tunnel from scheduler to render!"); + if (ilclient_change_component_state(m_comp[eVideoRender], OMX_StateExecuting) != 0) + esyslog("rpihddevice: failed to enable video render!"); + break; + } +} + +void cOmx::HandleBufferEmpty(COMPONENT_T *comp) +{ + if (comp == m_comp[eVideoDecoder]) + { + m_mutex->Lock(); + m_freeVideoBuffers++; + m_mutex->Unlock(); + } + else if (comp == m_comp[eAudioRender]) + { + m_mutex->Lock(); + m_freeAudioBuffers++; + m_mutex->Unlock(); + } +} + +void cOmx::OnBufferEmpty(void *instance, COMPONENT_T *comp) +{ + cOmx* omx = static_cast <cOmx*> (instance); + omx->HandleBufferEmpty(comp); +} + +void cOmx::OnPortSettingsChanged(void *instance, COMPONENT_T *comp, unsigned int data) +{ + cOmx* omx = static_cast <cOmx*> (instance); + omx->HandlePortSettingsChanged(data); +} + +void cOmx::OnEndOfStream(void *instance, COMPONENT_T *comp, unsigned int data) +{ + cOmx* omx = static_cast <cOmx*> (instance); + omx->HandleEndOfStream(data); +} + +void cOmx::OnError(void *instance, COMPONENT_T *comp, unsigned int data) +{ + if (data != OMX_ErrorSameState) + esyslog("rpihddevice: OmxError(%s)", errStr((int)data)); +} + +cOmx::cOmx() : + m_mutex(new cMutex()), + m_setVideoStartTime(true), + m_setAudioStartTime(true), + m_setVideoDiscontinuity(false), + m_freeAudioBuffers(0), + m_freeVideoBuffers(0), + m_clockReference(eClockRefAudio) +{ +} + +cOmx::~cOmx() +{ + delete m_mutex; +} + +int cOmx::Init(void) +{ + m_client = ilclient_init(); + if (m_client == NULL) + esyslog("rpihddevice: ilclient_init() failed!"); + + if (OMX_Init() != OMX_ErrorNone) + esyslog("rpihddevice: OMX_Init() failed!"); + + ilclient_set_error_callback(m_client, OnError, this); + ilclient_set_empty_buffer_done_callback(m_client, OnBufferEmpty, this); + ilclient_set_port_settings_callback(m_client, OnPortSettingsChanged, this); + ilclient_set_eos_callback(m_client, OnEndOfStream, this); + + // create video_decode + if (ilclient_create_component(m_client, &m_comp[eVideoDecoder], + "video_decode", (ILCLIENT_CREATE_FLAGS_T) + (ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS)) != 0) + esyslog("rpihddevice: failed creating video decoder!"); + + // create video_render + if (ilclient_create_component(m_client, &m_comp[eVideoRender], + "video_render", ILCLIENT_DISABLE_ALL_PORTS) != 0) + esyslog("rpihddevice: failed creating video render!"); + + //create clock + if (ilclient_create_component(m_client, &m_comp[eClock], + "clock", ILCLIENT_DISABLE_ALL_PORTS) != 0) + esyslog("rpihddevice: failed creating clock!"); + + // create audio_render + if (ilclient_create_component(m_client, &m_comp[eAudioRender], + "audio_render", (ILCLIENT_CREATE_FLAGS_T) + (ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS)) != 0) + esyslog("rpihddevice: failed creating audio render!"); + + //create video_scheduler + if (ilclient_create_component(m_client, &m_comp[eVideoScheduler], + "video_scheduler", ILCLIENT_DISABLE_ALL_PORTS) != 0) + esyslog("rpihddevice: failed creating video scheduler!"); + + // setup tunnels + set_tunnel(&m_tun[eVideoDecoderToVideoScheduler], + m_comp[eVideoDecoder], 131, m_comp[eVideoScheduler], 10); + + set_tunnel(&m_tun[eVideoSchedulerToVideoRender], + m_comp[eVideoScheduler], 11, m_comp[eVideoRender], 90); + + set_tunnel(&m_tun[eClockToVideoScheduler], + m_comp[eClock], 80, m_comp[eVideoScheduler], 12); + + set_tunnel(&m_tun[eClockToAudioRender], + m_comp[eClock], 81, m_comp[eAudioRender], 101); + + // setup clock tunnels first + if (ilclient_setup_tunnel(&m_tun[eClockToVideoScheduler], 0, 0) != 0) + esyslog("rpihddevice: failed to setup up tunnel from clock to video scheduler!"); + + if (ilclient_setup_tunnel(&m_tun[eClockToAudioRender], 0, 0) != 0) + esyslog("rpihddevice: failed to setup up tunnel from clock to audio render!"); + + ilclient_change_component_state(m_comp[eClock], OMX_StateExecuting); + ilclient_change_component_state(m_comp[eVideoDecoder], OMX_StateIdle); + + // set up the number and size of buffers for audio render + m_freeAudioBuffers = 2; //64; + OMX_PARAM_PORTDEFINITIONTYPE param; + 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 = 160 * 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!"); + + OMX_INIT_STRUCT(param); + param.nPortIndex = 130; + if (OMX_GetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]), + OMX_IndexParamPortDefinition, ¶m) != OMX_ErrorNone) + esyslog("rpihddevice: failed to get video decoder port parameters!"); + + m_freeVideoBuffers = param.nBufferCountActual; + dsyslog("rpihddevice: started with %d video and %d audio buffers", + m_freeVideoBuffers, m_freeAudioBuffers); + +/* // configure video decoder stall callback + OMX_CONFIG_BUFFERSTALLTYPE stallConf; + OMX_INIT_STRUCT(stallConf); + stallConf.nPortIndex = 131; + stallConf.nDelay = 1500 * 1000; + if (OMX_SetConfig(m_comp[eVideoDecoder], OMX_IndexConfigBufferStall, + &stallConf) != OMX_ErrorNone) + esyslog("rpihddevice: failed to set video decoder stall config!"); + + OMX_CONFIG_REQUESTCALLBACKTYPE reqCallback; + OMX_INIT_STRUCT(reqCallback); + reqCallback.nPortIndex = 131; + reqCallback.nIndex = OMX_IndexConfigBufferStall; + reqCallback.bEnable = OMX_TRUE; + if (OMX_SetConfig(m_comp[eVideoDecoder], OMX_IndexConfigRequestCallback, + &reqCallback) != OMX_ErrorNone) + esyslog("rpihddevice: failed to set video decoder stall callback!"); +*/ +// if (ilclient_enable_port_buffers(m_comp[eAudioRender], 100, NULL, NULL, NULL) != 0) +// esyslog("rpihddevice: failed to enable port buffer on audio render!"); + + OMX_PARAM_BRCMVIDEODECODEERRORCONCEALMENTTYPE ectype; + OMX_INIT_STRUCT(ectype); + ectype.bStartWithValidFrame = OMX_FALSE; + if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]), + OMX_IndexParamBrcmVideoDecodeErrorConcealment, &ectype) != OMX_ErrorNone) + esyslog("rpihddevice: failed to set video decode error concealment failed\n"); + + return 0; +} + +int cOmx::DeInit(void) +{ + ilclient_teardown_tunnels(m_tun); + ilclient_state_transition(m_comp, OMX_StateIdle); + ilclient_state_transition(m_comp, OMX_StateLoaded); + ilclient_cleanup_components(m_comp); + + OMX_Deinit(); + ilclient_destroy(m_client); + + return 0; +} + +OMX_TICKS cOmx::ToOmxTicks(int64_t val) +{ + OMX_TICKS ticks; + ticks.nLowPart = val; + ticks.nHighPart = val >> 32; + return ticks; +} + +int64_t cOmx::FromOmxTicks(OMX_TICKS &ticks) +{ + int64_t ret = ticks.nLowPart | ((int64_t)(ticks.nHighPart) << 32); + return ret; +} + +void cOmx::PtsToTicks(uint64_t pts, OMX_TICKS &ticks) +{ + // ticks = pts * OMX_TICKS_PER_SECOND / PTSTICKS + pts = pts * 100 / 9; + ticks.nLowPart = pts; + ticks.nHighPart = pts >> 32; +} + +uint64_t cOmx::TicksToPts(OMX_TICKS &ticks) +{ + // pts = ticks * PTSTICKS / OMX_TICKS_PER_SECOND + uint64_t pts = ticks.nHighPart; + pts = (pts << 32) + ticks.nLowPart; + pts = pts * 9 / 100; + return pts; +} + +int64_t cOmx::GetSTC(void) +{ + int64_t stc = -1; +// return stc; + + OMX_TIME_CONFIG_TIMESTAMPTYPE timestamp; + OMX_INIT_STRUCT(timestamp); + timestamp.nPortIndex = OMX_ALL; + + if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eClock]), + OMX_IndexConfigTimeCurrentMediaTime, ×tamp) != OMX_ErrorNone) + esyslog("rpihddevice: failed get current clock reference!"); + else + stc = TicksToPts(timestamp.nTimestamp); + +// dsyslog("S %u", timestamp.nTimestamp.nLowPart); + + return stc; +} + +bool cOmx::IsClockRunning(void) +{ + OMX_TIME_CONFIG_CLOCKSTATETYPE cstate; + OMX_INIT_STRUCT(cstate); + + if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eClock]), + OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) + esyslog("rpihddevice: failed get clock state!"); + + if (cstate.eState == OMX_TIME_ClockStateRunning) + return true; + else + return false; +} + +void cOmx::SetClockState(eClockState clockState) +{ + m_mutex->Lock(); + + dsyslog("rpihddevice: SetClockState(%s)", + clockState == eClockStateRun ? "eClockStateRun" : + clockState == eClockStateStop ? "eClockStateStop" : + clockState == eClockStateWaitForVideo ? "eClockStateWaitForVideo" : + clockState == eClockStateWaitForAudio ? "eClockStateWaitForAudio" : + clockState == eClockStateWaitForAudioVideo ? "eClockStateWaitForAudioVideo" : + "unknown"); + + OMX_TIME_CONFIG_CLOCKSTATETYPE cstate; + OMX_INIT_STRUCT(cstate); + + if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eClock]), + OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) + esyslog("rpihddevice: failed to get clock state!"); + +/* dsyslog("rpihddevice: current clock state: %s", + cstate.eState == OMX_TIME_ClockStateRunning ? "Running" : + cstate.eState == OMX_TIME_ClockStateStopped ? "Stopped" : + cstate.eState == OMX_TIME_ClockStateWaitingForStartTime ? "WaitingForStartTime" : + "unknown"); +*/ + if ((cstate.eState == OMX_TIME_ClockStateRunning) && + (clockState == eClockStateWaitForVideo || + clockState == eClockStateWaitForAudio || + clockState == eClockStateWaitForAudioVideo)) + { + // clock already running, need to stop it first + cstate.eState = OMX_TIME_ClockStateStopped; + if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]), + OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) + esyslog("rpihddevice: failed to stop clock!"); + } + + switch (clockState) + { + case eClockStateRun: + cstate.eState = OMX_TIME_ClockStateRunning; + break; + + case eClockStateStop: + cstate.eState = OMX_TIME_ClockStateStopped; + break; + + case eClockStateWaitForVideo: + cstate.eState = OMX_TIME_ClockStateWaitingForStartTime; + cstate.nWaitMask = OMX_CLOCKPORT0; + m_setVideoStartTime = true; + SetClockReference(eClockRefVideo); + break; + + case eClockStateWaitForAudio: + cstate.eState = OMX_TIME_ClockStateWaitingForStartTime; + cstate.nWaitMask = OMX_CLOCKPORT1; + m_setAudioStartTime = true; + SetClockReference(eClockRefAudio); + break; + + case eClockStateWaitForAudioVideo: + cstate.eState = OMX_TIME_ClockStateWaitingForStartTime; + cstate.nWaitMask = OMX_CLOCKPORT0 | OMX_CLOCKPORT1; + m_setVideoStartTime = true; + m_setAudioStartTime = true; + SetClockReference(eClockRefAudio); + break; + } + + if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]), + OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) + esyslog("rpihddevice: failed to set clock state!"); + + m_mutex->Unlock(); +} + +void cOmx::SetClockScale(float scale) +{ + OMX_TIME_CONFIG_SCALETYPE scaleType; + 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) + esyslog("rpihddevice: failed to set clock scale (%d)!", scaleType.xScale); + else + dsyslog("rpihddevice: set clock scale to %.2f (%d)", scale, scaleType.xScale); +} + +void cOmx::SetMediaTime(uint64_t pts) +{ + OMX_TIME_CONFIG_TIMESTAMPTYPE timeStamp; + OMX_INIT_STRUCT(timeStamp); + timeStamp.nPortIndex = m_clockReference == eClockRefAudio ? 81 : 80; + cOmx::PtsToTicks(pts, timeStamp.nTimestamp); + + if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]), + /*m_clockReference == eClockRefAudio ? + OMX_IndexConfigTimeCurrentAudioReference : + OMX_IndexConfigTimeCurrentVideoReference*/ + OMX_IndexConfigTimeClientStartTime, &timeStamp) != OMX_ErrorNone) + esyslog("rpihddevice: failed to set %s media clock reference!", + m_clockReference == eClockRefAudio ? "audio" : "video"); +} + +unsigned int cOmx::GetMediaTime(void) +{ + unsigned int ret = 0; + + OMX_TIME_CONFIG_TIMESTAMPTYPE timestamp; + OMX_INIT_STRUCT(timestamp); + timestamp.nPortIndex = OMX_ALL; + + if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eClock]), + OMX_IndexConfigTimeCurrentMediaTime, ×tamp) != OMX_ErrorNone) + esyslog("rpihddevice: failed get current clock reference!"); + else + ret = timestamp.nTimestamp.nLowPart; + + return ret; +} + +void cOmx::SetClockReference(eClockReference clockReference) +{ + m_clockReference = clockReference; + + OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE refClock; + OMX_INIT_STRUCT(refClock); + refClock.eClock = (m_clockReference == eClockRefAudio) ? + OMX_TIME_RefClockAudio : OMX_TIME_RefClockVideo; + + if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]), + OMX_IndexConfigTimeActiveRefClock, &refClock) != OMX_ErrorNone) + esyslog("rpihddevice: failed set active clock reference!"); +} + +void cOmx::SetVolume(int vol) +{ + dsyslog("rpihddevice: SetVolumeDevice(%d)", vol); + + OMX_AUDIO_CONFIG_VOLUMETYPE volume; + OMX_INIT_STRUCT(volume); + volume.nPortIndex = 100; + volume.bLinear = OMX_TRUE; + volume.sVolume.nValue = vol * 100 / 255; + + if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eAudioRender]), + OMX_IndexConfigAudioVolume, &volume) != OMX_ErrorNone) + esyslog("rpihddevice: failed to set volume!"); +} + +void cOmx::SendEos(void) +{ +#if 0 + OMX_BUFFERHEADERTYPE *buf = ilclient_get_input_buffer(m_comp[eVideoDecoder], 130, 1); + if (buf == NULL) + return; + + buf->nFilledLen = 0; + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS; + + if (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_comp[eVideoDecoder]), buf) != OMX_ErrorNone) + esyslog("rpihddevice: failed to send empty packet to video decoder!"); + + if (!m_eosEvent->Wait(10000)) + esyslog("rpihddevice: time out waiting for EOS event!"); +#endif +} + +void cOmx::Stop(void) +{ + // put video decoder into idle + ilclient_change_component_state(m_comp[eVideoDecoder], OMX_StateIdle); + + // put video scheduler into idle + ilclient_flush_tunnels(&m_tun[eVideoDecoderToVideoScheduler], 1); + ilclient_disable_tunnel(&m_tun[eVideoDecoderToVideoScheduler]); + ilclient_flush_tunnels(&m_tun[eClockToVideoScheduler], 1); + ilclient_disable_tunnel(&m_tun[eClockToVideoScheduler]); + ilclient_change_component_state(m_comp[eVideoScheduler], OMX_StateIdle); + + // put video render into idle + ilclient_flush_tunnels(&m_tun[eVideoSchedulerToVideoRender], 1); + ilclient_disable_tunnel(&m_tun[eVideoSchedulerToVideoRender]); + ilclient_change_component_state(m_comp[eVideoRender], OMX_StateIdle); + + // 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); + + // disable port buffers and allow video decoder and audio render to reconfig + ilclient_disable_port_buffers(m_comp[eVideoDecoder], 130, NULL, NULL, NULL); + ilclient_disable_port_buffers(m_comp[eAudioRender], 100, NULL, NULL, NULL); + + SetClockState(eClockStateStop); +} + +void cOmx::FlushAudio(void) +{ + ilclient_flush_tunnels(&m_tun[eClockToAudioRender], 1); + + if (OMX_SendCommand(ILC_GET_HANDLE(m_comp[eAudioRender]), OMX_CommandFlush, 100, NULL) != OMX_ErrorNone) + esyslog("rpihddevice: failed to flush audio render!"); + + m_setAudioStartTime = true; +} + +void cOmx::FlushVideo(bool flushRender) +{ + ilclient_flush_tunnels(&m_tun[eClockToVideoScheduler], 1); + + if (OMX_SendCommand(ILC_GET_HANDLE(m_comp[eVideoDecoder]), OMX_CommandFlush, 130, NULL) != OMX_ErrorNone) + esyslog("rpihddevice: failed to flush video decoder!"); + + ilclient_flush_tunnels(&m_tun[eVideoDecoderToVideoScheduler], 1); + + if (flushRender) + ilclient_flush_tunnels(&m_tun[eVideoSchedulerToVideoRender], 1); + + m_setVideoStartTime = true; + m_setVideoDiscontinuity = true; +} + +int cOmx::SetVideoCodec(cVideoCodec::eCodec codec) +{ + // configure video decoder + OMX_VIDEO_PARAM_PORTFORMATTYPE videoFormat; + OMX_INIT_STRUCT(videoFormat); + videoFormat.nPortIndex = 130; + videoFormat.eCompressionFormat = + codec == cVideoCodec::eMPEG2 ? OMX_VIDEO_CodingMPEG2 : + codec == cVideoCodec::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!"); + + if (ilclient_enable_port_buffers(m_comp[eVideoDecoder], 130, NULL, NULL, NULL) != 0) + esyslog("rpihddevice: failed to enable port buffer on video decoder!"); + + if (ilclient_change_component_state(m_comp[eVideoDecoder], OMX_StateExecuting) != 0) + esyslog("rpihddevice: failed to set video decoder to executing state!"); + + // setup clock tunnels first + if (ilclient_setup_tunnel(&m_tun[eClockToVideoScheduler], 0, 0) != 0) + esyslog("rpihddevice: failed to setup up tunnel from clock to video scheduler!"); + + return 0; +} + +int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels, int samplingRate, + cAudioPort::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); + + if (OMX_SendCommand(ILC_GET_HANDLE(m_comp[eAudioRender]), OMX_CommandFlush, 100, NULL) != OMX_ErrorNone) + esyslog("rpihddevice: failed to flush audio render!"); + + 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 == cAudioCodec::ePCM ? OMX_AUDIO_CodingPCM : + outputFormat == cAudioCodec::eMPG ? OMX_AUDIO_CodingMP3 : + outputFormat == cAudioCodec::eAC3 ? OMX_AUDIO_CodingDDP : + outputFormat == cAudioCodec::eEAC3 ? OMX_AUDIO_CodingDDP : + outputFormat == cAudioCodec::eAAC ? OMX_AUDIO_CodingAAC : + outputFormat == cAudioCodec::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 cAudioCodec::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 cAudioCodec::eAC3: + case cAudioCodec::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 cAudioCodec::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 cAudioCodec::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 cAudioCodec::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: + esyslog("rpihddevice: output codec not supported: %s!", + cAudioCodec::Str(outputFormat)); + break; + } + + OMX_CONFIG_BRCMAUDIODESTINATIONTYPE audioDest; + OMX_INIT_STRUCT(audioDest); + strcpy((char *)audioDest.sName, + audioPort == cAudioPort::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!"); + + ilclient_change_component_state(m_comp[eAudioRender], OMX_StateExecuting); + + if (ilclient_setup_tunnel(&m_tun[eClockToAudioRender], 0, 0) != 0) + esyslog("rpihddevice: failed to setup up tunnel from clock to video scheduler!"); + + return 0; +} + +OMX_BUFFERHEADERTYPE* cOmx::GetAudioBuffer(uint64_t pts) +{ + m_mutex->Lock(); + OMX_BUFFERHEADERTYPE* buf = NULL; + + if (m_freeAudioBuffers > 0) + { + buf = ilclient_get_input_buffer(m_comp[eAudioRender], 100, 0); + if (buf) + { + cOmx::PtsToTicks(pts, buf->nTimeStamp); + buf->nFlags = m_setAudioStartTime ? OMX_BUFFERFLAG_STARTTIME : 0; + buf->nFlags |= pts ? 0 : OMX_BUFFERFLAG_TIME_UNKNOWN; + + m_setAudioStartTime = false; + m_freeAudioBuffers--; + } + } + m_mutex->Unlock(); + return buf; +} + +OMX_BUFFERHEADERTYPE* cOmx::GetVideoBuffer(uint64_t pts) +{ + m_mutex->Lock(); + OMX_BUFFERHEADERTYPE* buf = NULL; + + if (m_freeVideoBuffers > 0) + { + buf = ilclient_get_input_buffer(m_comp[eVideoDecoder], 130, 0); + if (buf) + { + cOmx::PtsToTicks(pts, buf->nTimeStamp); + buf->nFlags = m_setVideoStartTime ? + OMX_BUFFERFLAG_STARTTIME : OMX_BUFFERFLAG_TIME_UNKNOWN; + buf->nFlags |= m_setVideoDiscontinuity ? OMX_BUFFERFLAG_DISCONTINUITY : 0; + + m_setVideoStartTime = false; + m_setVideoDiscontinuity = false; + m_freeVideoBuffers--; + } + } + m_mutex->Unlock(); + return buf; +} + +bool cOmx::PollVideoBuffers(int minBuffers) +{ + return (m_freeVideoBuffers > minBuffers); +} + +bool cOmx::PollAudioBuffers(int minBuffers) +{ + return (m_freeAudioBuffers > minBuffers); +} + +bool cOmx::EmptyAudioBuffer(OMX_BUFFERHEADERTYPE *buf) +{ + if (!buf) + return false; + + return (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_comp[eAudioRender]), buf) + == OMX_ErrorNone); +} + +bool cOmx::EmptyVideoBuffer(OMX_BUFFERHEADERTYPE *buf) +{ + if (!buf) + return false; + + return (OMX_EmptyThisBuffer(ILC_GET_HANDLE(m_comp[eVideoDecoder]), buf) + == OMX_ErrorNone); +} |