diff options
author | Thomas Reufer <thomas@reufer.ch> | 2014-02-10 21:53:25 +0100 |
---|---|---|
committer | Thomas Reufer <thomas@reufer.ch> | 2014-02-10 21:53:25 +0100 |
commit | 0094472cda6eefa9b5363ad844daf0fcfd00c326 (patch) | |
tree | b577f931820ef3899da856f9ccd0de04f4c8ffa8 | |
parent | 88f137d194b1768344e954a1b1d35fb1fce03df9 (diff) | |
download | vdr-plugin-rpihddevice-0.0.8.tar.gz vdr-plugin-rpihddevice-0.0.8.tar.bz2 |
2014-02-10: Version 0.0.80.0.8
-------------------------
- new:
- image grabbing
- implemented proper handling in case of buffer stall
- reporting video size
- support letter box and center cut out set by VDR
- support video scaling
- fixed:
- increased number of audio buffer to fix replay issues with PES recordings
- return correct number of audio bytes written from PlayAudio()
- fixed start up in audio only mode
- fixed still image with deinterlacer
- fixed crash during deinitialization
- fixed crash when copying 5.1 PCM audio
- use cThread::mutex for locking
- implement cOvgOsd::SetAreas() and cOvgOsd::SetActive()
- audio codec clean up, drop AAC-LATM and rename ADTS to AAC
- audio decoding thread clean up
- known issues
- StillImage() will cause buffer stall
- artifacts with StillImage() and PES recordings
- speed to fast when fast replaying audio only recordings
-rw-r--r-- | .project | 79 | ||||
-rw-r--r-- | HISTORY | 24 | ||||
-rw-r--r-- | Makefile | 9 | ||||
-rw-r--r-- | audio.c | 407 | ||||
-rw-r--r-- | audio.h | 9 | ||||
-rw-r--r-- | display.c | 106 | ||||
-rw-r--r-- | display.h | 32 | ||||
-rw-r--r-- | omx.c | 386 | ||||
-rw-r--r-- | omx.h | 26 | ||||
-rw-r--r-- | omxdevice.c | 192 | ||||
-rw-r--r-- | omxdevice.h | 20 | ||||
-rw-r--r-- | ovgosd.c | 137 | ||||
-rw-r--r-- | ovgosd.h | 17 | ||||
-rw-r--r-- | po/de_DE.po | 5 | ||||
-rw-r--r-- | rpihddevice.c | 10 | ||||
-rw-r--r-- | setup.c | 105 | ||||
-rw-r--r-- | setup.h | 18 | ||||
-rw-r--r-- | tools.h | 90 |
18 files changed, 1113 insertions, 559 deletions
diff --git a/.project b/.project new file mode 100644 index 0000000..b385bd0 --- /dev/null +++ b/.project @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>rpihddevice</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name> + <triggers>clean,full,incremental,</triggers> + <arguments> + <dictionary> + <key>?name?</key> + <value></value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.append_environment</key> + <value>true</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.autoBuildTarget</key> + <value>all</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.buildArguments</key> + <value></value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.buildCommand</key> + <value>make</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.cleanBuildTarget</key> + <value>clean</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.contents</key> + <value>org.eclipse.cdt.make.core.activeConfigSettings</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.enableAutoBuild</key> + <value>false</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.enableCleanBuild</key> + <value>true</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.enableFullBuild</key> + <value>true</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.fullBuildTarget</key> + <value>all</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.stopOnError</key> + <value>true</value> + </dictionary> + <dictionary> + <key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key> + <value>true</value> + </dictionary> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name> + <triggers>full,incremental,</triggers> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.cdt.core.cnature</nature> + <nature>org.eclipse.cdt.core.ccnature</nature> + <nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature> + <nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature> + </natures> +</projectDescription> @@ -1,6 +1,30 @@ VDR Plugin 'rpihddevice' Revision History ----------------------------------------- +2014-02-10: Version 0.0.8 +------------------------- +- new: + - image grabbing + - implemented proper handling in case of buffer stall + - reporting video size + - support letter box and center cut out set by VDR + - support video scaling +- fixed: + - increased number of audio buffer to fix replay issues with PES recordings + - return correct number of audio bytes written from PlayAudio() + - fixed start up in audio only mode + - fixed still image with deinterlacer + - fixed crash during deinitialization + - fixed crash when copying 5.1 PCM audio + - use cThread::mutex for locking + - implement cOvgOsd::SetAreas() and cOvgOsd::SetActive() + - audio codec clean up, drop AAC-LATM and rename ADTS to AAC + - audio decoding thread clean up +- known issues + - StillImage() will cause buffer stall + - artifacts with StillImage() and PES recordings + - speed to fast when fast replaying audio only recordings + 2013-12-30: Version 0.0.7 ------------------------- - new: @@ -16,7 +16,7 @@ VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ pri ### The directory environment: # Use package data if installed...otherwise assume we're under the VDR source directory: -PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell pkg-config --variable=$(1) vdr || pkg-config --variable=$(1) ../../../vdr.pc)) +PKGCFG = $(if $(VDRDIR),$(shell pkg-config --variable=$(1) $(VDRDIR)/vdr.pc),$(shell PKG_CONFIG_PATH="$$PKG_CONFIG_PATH:../../.." pkg-config --variable=$(1) vdr)) LIBDIR = $(call PKGCFG,libdir) LOCDIR = $(call PKGCFG,locdir) PLGCFG = $(call PKGCFG,plgcfg) @@ -62,10 +62,15 @@ INCLUDES += -I$(ILCDIR) -I$(VCINCDIR) -I$(VCINCDIR)/interface/vcos/pthreads -I$( 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 +DEBUG ?= 0 +ifeq ($(DEBUG), 1) + DEFINES += -DDEBUG +endif + ### The object files (add further files here): ILCLIENT = $(ILCDIR)/libilclient.a -OBJS = $(PLUGIN).o setup.o omx.o audio.o omxdevice.o ovgosd.o +OBJS = $(PLUGIN).o setup.o omx.o audio.o omxdevice.o ovgosd.o display.o ### The main target: @@ -20,8 +20,15 @@ class cAudioParser public: - cAudioParser() { } - ~cAudioParser() { } + cAudioParser() + { + m_mutex = new cMutex(); + } + + ~cAudioParser() + { + delete m_mutex; + } AVPacket* Packet(void) { @@ -85,19 +92,26 @@ public: bool Append(const unsigned char *data, unsigned int length) { - if (m_size + length + FF_INPUT_BUFFER_PADDING_SIZE > AVPKT_BUFFER_SIZE) - return false; - - memcpy(m_packet.data + m_size, data, length); - m_size += length; - memset(m_packet.data + m_size, 0, FF_INPUT_BUFFER_PADDING_SIZE); + m_mutex->Lock(); + bool ret = true; - m_parsed = false; - return true; + if (m_size + length + FF_INPUT_BUFFER_PADDING_SIZE > AVPKT_BUFFER_SIZE) + ret = false; + else + { + memcpy(m_packet.data + m_size, data, length); + m_size += length; + memset(m_packet.data + m_size, 0, FF_INPUT_BUFFER_PADDING_SIZE); + m_parsed = false; + } + m_mutex->Unlock(); + return ret; } void Shrink(unsigned int length) { + m_mutex->Lock(); + if (length < m_size) { memmove(m_packet.data, m_packet.data + length, m_size - length); @@ -108,6 +122,8 @@ public: } else Reset(); + + m_mutex->Unlock(); } private: @@ -126,6 +142,7 @@ private: void Parse() { + m_mutex->Lock(); cAudioCodec::eCodec codec = cAudioCodec::eInvalid; int channels = 0; int offset = 0; @@ -159,15 +176,9 @@ private: codec = cAudioCodec::eEAC3; } break; - case cAudioCodec::eAAC: - if (LatmCheck(p, n, frameSize, channels, samplingRate)) - codec = cAudioCodec::eAAC; - break; - - case cAudioCodec::eADTS: if (AdtsCheck(p, n, frameSize, channels, samplingRate)) - codec = cAudioCodec::eADTS; + codec = cAudioCodec::eAAC; break; default: @@ -196,7 +207,7 @@ private: if (offset) { - dsyslog("rpihddevice: audio parser skipped %d of %d bytes", offset, m_size); + DLOG("audio parser skipped %d of %d bytes", offset, m_size); Shrink(offset); } @@ -209,8 +220,10 @@ private: } m_parsed = true; + m_mutex->Unlock(); } + cMutex* m_mutex; AVPacket m_packet; cAudioCodec::eCodec m_codec; unsigned int m_channels; @@ -232,8 +245,7 @@ private: { return FastMpegCheck(p) ? cAudioCodec::eMPG : FastAc3Check (p) ? cAudioCodec::eAC3 : - FastLatmCheck(p) ? cAudioCodec::eAAC : - FastAdtsCheck(p) ? cAudioCodec::eADTS : + FastAdtsCheck(p) ? cAudioCodec::eAAC : cAudioCodec::eInvalid; } @@ -600,7 +612,6 @@ cAudioDecoder::cAudioDecoder(cOmx *omx) : m_reset(false), m_ready(false), m_pts(0), - m_mutex(new cMutex()), m_wait(new cCondWait()), m_parser(new cAudioParser()), m_omx(omx) @@ -611,7 +622,6 @@ cAudioDecoder::~cAudioDecoder() { delete m_parser; delete m_wait; - delete m_mutex; } int cAudioDecoder::Init(void) @@ -626,8 +636,7 @@ int cAudioDecoder::Init(void) m_codecs[cAudioCodec::eMPG ].codec = avcodec_find_decoder(CODEC_ID_MP3); m_codecs[cAudioCodec::eAC3 ].codec = avcodec_find_decoder(CODEC_ID_AC3); m_codecs[cAudioCodec::eEAC3].codec = avcodec_find_decoder(CODEC_ID_EAC3); - m_codecs[cAudioCodec::eAAC ].codec = avcodec_find_decoder(CODEC_ID_AAC_LATM); - m_codecs[cAudioCodec::eADTS].codec = avcodec_find_decoder(CODEC_ID_AAC); + m_codecs[cAudioCodec::eAAC].codec = avcodec_find_decoder(CODEC_ID_AAC); for (int i = 0; i < cAudioCodec::eNumCodecs; i++) { @@ -637,13 +646,13 @@ int cAudioDecoder::Init(void) m_codecs[codec].context = avcodec_alloc_context3(m_codecs[codec].codec); if (!m_codecs[codec].context) { - esyslog("rpihddevice: failed to allocate %s context!", cAudioCodec::Str(codec)); + ELOG("failed to allocate %s context!", cAudioCodec::Str(codec)); ret = -1; break; } if (avcodec_open2(m_codecs[codec].context, m_codecs[codec].codec, NULL) < 0) { - esyslog("rpihddevice: failed to open %s decoder!", cAudioCodec::Str(codec)); + ELOG("failed to open %s decoder!", cAudioCodec::Str(codec)); ret = -1; break; } @@ -660,266 +669,254 @@ int cAudioDecoder::Init(void) int cAudioDecoder::DeInit(void) { - Cancel(); + Lock(); + + Reset(); + Cancel(-1); + m_wait->Signal(); + + while (Active()) + cCondWait::SleepMs(50); for (int i = 0; i < cAudioCodec::eNumCodecs; i++) { cAudioCodec::eCodec codec = static_cast<cAudioCodec::eCodec>(i); - avcodec_close(m_codecs[codec].context); - av_free(m_codecs[codec].context); + if (m_codecs[codec].codec) + { + avcodec_close(m_codecs[codec].context); + av_free(m_codecs[codec].context); + } } m_parser->DeInit(); + + Unlock(); return 0; } -int cAudioDecoder::WriteData(const unsigned char *buf, unsigned int length, uint64_t pts) +bool cAudioDecoder::WriteData(const unsigned char *buf, unsigned int length, uint64_t pts) { - int ret = 0; + Lock(); + bool ret = false; + if (m_ready) { - m_mutex->Lock(); if (m_parser->Append(buf, length)) { m_pts = pts; m_ready = false; m_wait->Signal(); - ret = length; + ret = true; } - m_mutex->Unlock(); } + Unlock(); return ret; } void cAudioDecoder::Reset(void) { - m_mutex->Lock(); + Lock(); + m_reset = true; m_wait->Signal(); - m_mutex->Unlock(); + while (m_reset) + cCondWait::SleepMs(1); + + Unlock(); } bool cAudioDecoder::Poll(void) { - return m_ready && m_omx->PollAudioBuffers(); + return m_ready; } void cAudioDecoder::Action(void) { - dsyslog("rpihddevice: cAudioDecoder() thread started"); + DLOG("cAudioDecoder() thread started"); - while (Running()) - { - unsigned int channels = 0; - unsigned int outputChannels = 0; - unsigned int samplingRate = 0; + unsigned int channels = 0; + unsigned int outputChannels = 0; + unsigned int samplingRate = 0; + bool setupChanged = true; - bool bufferFull = false; - bool setupChanged = false; + cAudioCodec::eCodec codec = cAudioCodec::eInvalid; + OMX_BUFFERHEADERTYPE *buf = 0; - cAudioCodec::eCodec codec = cAudioCodec::eInvalid; - OMX_BUFFERHEADERTYPE *buf = 0; + AVFrame *frame = avcodec_alloc_frame(); + if (!frame) + { + ELOG("failed to allocate audio frame!"); + return; + } - AVFrame *frame = avcodec_alloc_frame(); - if (!frame) - { - esyslog("rpihddevice: failed to allocate audio frame!"); - return; - } + m_ready = true; - m_reset = false; - m_ready = true; + while (Running()) + { + setupChanged |= cRpiSetup::HasAudioSetupChanged(); - while (!m_reset) + // test for codec change if there is data in parser and no left over + if (!m_parser->Empty() && !frame->nb_samples) + setupChanged |= codec != m_parser->GetCodec() || + channels != m_parser->GetChannels() || + samplingRate != m_parser->GetSamplingRate(); + + // if necessary, set up audio codec + if (setupChanged) { - setupChanged |= cRpiSetup::HasAudioSetupChanged(); + codec = m_parser->GetCodec(); + channels = m_parser->GetChannels(); + samplingRate = m_parser->GetSamplingRate(); - // check for data if no decoded samples are pending - if (!m_parser->Empty() && !frame->nb_samples) - { - if (setupChanged || - codec != m_parser->GetCodec() || - channels != m_parser->GetChannels() || - samplingRate != m_parser->GetSamplingRate()) - { - // to change codec config, we need to empty buffer first - if (buf) - bufferFull = true; - else - { - codec = m_parser->GetCodec(); - channels = m_parser->GetChannels(); - samplingRate = m_parser->GetSamplingRate(); + outputChannels = channels; + SetCodec(codec, outputChannels, samplingRate); - outputChannels = channels; - SetCodec(codec, outputChannels, samplingRate); + avcodec_get_frame_defaults(frame); + setupChanged = false; - setupChanged = false; - } - } - } + if (codec == cAudioCodec::eInvalid) + m_reset = true; + } - // if codec has been configured but we don't have a buffer, get one - while (codec != cAudioCodec::eInvalid && !buf && !m_reset) + // get free buffer + while ((!m_parser->Empty() || frame->nb_samples) && !buf && !m_reset) + { + buf = m_omx->GetAudioBuffer(m_pts); + if (!buf) + m_wait->Wait(10); + else { - buf = m_omx->GetAudioBuffer(m_pts); - if (buf) - m_pts = 0; - else - m_wait->Wait(10); + m_pts = 0; + buf->nFilledLen = 0; } + } - // we have a non-full buffer and data to encode / copy - if (buf && !bufferFull && !m_parser->Empty()) + // decoding loop + while ((!m_parser->Empty() || frame->nb_samples) && buf && !m_reset) + { + if (setupChanged |= (codec != m_parser->GetCodec() || + channels != m_parser->GetChannels() || + samplingRate != m_parser->GetSamplingRate())) + break; + + // -- decode frame -- + if (!frame->nb_samples && !m_passthrough) { - int copied = 0; - if (m_passthrough) + // if frame has been emptied, decode new data, rise reset + // flag if something goes wrong + int gotFrame = 0; + int len = avcodec_decode_audio4(m_codecs[codec].context, + frame, &gotFrame, m_parser->Packet()); + if (len > 0) + m_parser->Shrink(len); + + if (len < 0) { - // for pass-through directly copy AV packet to buffer - if (m_parser->Packet()->size <= buf->nAllocLen - buf->nFilledLen) - { - m_mutex->Lock(); - - memcpy(buf->pBuffer + buf->nFilledLen, - m_parser->Packet()->data, m_parser->Packet()->size); - buf->nFilledLen += m_parser->Packet()->size; - m_parser->Shrink(m_parser->Packet()->size); - - m_mutex->Unlock(); - } - else - if (m_parser->Packet()->size > buf->nAllocLen) - { - esyslog("rpihddevice: encoded audio frame too big!"); - m_reset = true; - break; - } - else - bufferFull = true; + ELOG("failed to decode audio frame!"); + m_reset = true; + break; } - else - { - // decode frame if we do not pass-through - m_mutex->Lock(); - - int gotFrame = 0; - int len = avcodec_decode_audio4(m_codecs[codec].context, - frame, &gotFrame, m_parser->Packet()); - - if (len > 0) - m_parser->Shrink(len); + } - m_mutex->Unlock(); + // -- get length -- + int len = m_passthrough ? m_parser->Packet()->size : + av_samples_get_buffer_size(NULL, + outputChannels == 6 ? 8 : outputChannels, frame->nb_samples, + m_codecs[codec].context->sample_fmt, 1); - if (len < 0) - { - esyslog("rpihddevice: failed to decode audio frame!"); - m_reset = true; - break; - } - } + if (len > (signed)(buf->nAllocLen - buf->nFilledLen) || len < 0) + { + // rise reset flag if packet is even bigger than allocated buffer + m_reset = len > (signed)(buf->nAllocLen) || len < 0; + if (m_reset) + ELOG("encoded audio frame too big!"); + break; } - // we have decoded samples we need to copy to buffer - if (buf && !bufferFull && frame->nb_samples > 0) + // -- copy frame -- + if (m_passthrough) { - int length = av_samples_get_buffer_size(NULL, - outputChannels == 6 ? 8 : outputChannels, frame->nb_samples, - m_codecs[codec].context->sample_fmt, 1); + // for pass-through directly copy AV packet to buffer + memcpy(buf->pBuffer + buf->nFilledLen, + m_parser->Packet()->data, len); - if (length <= buf->nAllocLen - buf->nFilledLen) + buf->nFilledLen += len; + m_parser->Shrink(len); + } + else if (frame->nb_samples) + { + if (outputChannels == 6) { - if (outputChannels == 6) - { - // interleaved copy to fit 5.1 data into 8 channels - int32_t* src = (int32_t*)frame->data[0]; - int32_t* dst = (int32_t*)buf->pBuffer + buf->nFilledLen; - - for (int i = 0; i < frame->nb_samples; i++) - { - *dst++ = *src++; // LF & RF - *dst++ = *src++; // CF & LFE - *dst++ = *src++; // LR & RR - *dst++ = 0; // empty channels - } - } - else - memcpy(buf->pBuffer + buf->nFilledLen, frame->data[0], length); + int32_t* src = (int32_t*)frame->data[0]; + int32_t* dst = (int32_t*)(buf->pBuffer + buf->nFilledLen); - buf->nFilledLen += length; - frame->nb_samples = 0; - } - else - { - if (length > buf->nAllocLen) + // interleaved copy to fit 5.1 data into 8 channels + for (int i = 0; i < frame->nb_samples; i++) { - esyslog("rpihddevice: decoded audio frame too big!"); - m_reset = true; - break; + *dst++ = *src++; // LF & RF + *dst++ = *src++; // CF & LFE + *dst++ = *src++; // LR & RR + *dst++ = 0; // empty channels } - else - bufferFull = true; } - } - - // check if no decoded samples are pending and parser is empty - if (!frame->nb_samples && m_parser->Empty()) - { - // if no more data but buffer with data -> end of PES packet - if (buf && buf->nFilledLen > 0) - bufferFull = true; else - { - m_ready = true; - m_wait->Wait(50); - } + memcpy(buf->pBuffer + buf->nFilledLen, frame->data[0], len); + + buf->nFilledLen += len; + avcodec_get_frame_defaults(frame); } + } - // we have a buffer to empty - if (buf && bufferFull) + // -- reset -- + if (m_reset) + { + m_parser->Reset(); + avcodec_get_frame_defaults(frame); + if (buf) { - // send empty buffer if we're about to reset - if (m_reset) - buf->nFilledLen = 0; - - if (m_omx->EmptyAudioBuffer(buf)) - { - bufferFull = false; - buf = 0; - - // if parser is empty, get new data - if (m_parser->Empty()) - m_ready = true; - } - else - { - esyslog("rpihddevice: failed to empty audio buffer!"); - m_reset = true; - break; - } + cOmx::PtsToTicks(0, buf->nTimeStamp); + buf->nFilledLen = 0; + buf->nFlags = 0; } } - if (buf && m_omx->EmptyAudioBuffer(buf)) - buf = 0; + // -- empty buffer -- + if (buf) + { + if (buf->nFilledLen) + m_omx->SetClockReference(cOmx::eClockRefAudio); + if (m_omx->EmptyAudioBuffer(buf)) + buf = 0; + } + + m_reset = false; - av_free(frame); - m_parser->Reset(); + // accept new packets as soon as parser is empty and frame has been sent + if (m_parser->Empty() && !frame->nb_samples) + { + if (m_ready) + m_wait->Wait(50); + else + m_ready = true; + } } - dsyslog("rpihddevice: cAudioDecoder() thread ended"); + + av_free(frame); + SetCodec(cAudioCodec::eInvalid, outputChannels, samplingRate); + DLOG("cAudioDecoder() thread ended"); } void cAudioDecoder::SetCodec(cAudioCodec::eCodec codec, unsigned int &channels, unsigned int samplingRate) { + m_omx->StopAudio(); + if (codec != cAudioCodec::eInvalid && channels > 0) { - dsyslog("rpihddevice: set audio codec to %dch %s", - channels, cAudioCodec::Str(codec)); + DLOG("set audio codec to %dch %s", channels, cAudioCodec::Str(codec)); avcodec_flush_buffers(m_codecs[codec].context); m_codecs[codec].context->request_channel_layout = AV_CH_LAYOUT_NATIVE; m_codecs[codec].context->request_channels = 0; - //m_codecs[codec].context->bit_rate = 0; m_passthrough = false; cAudioCodec::eCodec outputFormat = cAudioCodec::ePCM; @@ -950,7 +947,7 @@ void cAudioDecoder::SetCodec(cAudioCodec::eCodec codec, unsigned int &channels, } m_omx->SetupAudioRender(outputFormat, channels, outputPort, samplingRate); - dsyslog("rpihddevice: set %s audio output format to %dch %s, %d.%dkHz%s", + ILOG("set %s audio output format to %dch %s, %d.%dkHz%s", cAudioPort::Str(outputPort), channels, cAudioCodec::Str(outputFormat), samplingRate / 1000, (samplingRate % 1000) / 100, m_passthrough ? " (pass-through)" : ""); @@ -13,7 +13,7 @@ extern "C" { #include <vdr/thread.h> -#include "types.h" +#include "tools.h" #include "omx.h" class cAudioParser; @@ -29,7 +29,7 @@ public: virtual int Init(void); virtual int DeInit(void); - virtual int WriteData(const unsigned char *buf, unsigned int length, uint64_t pts = 0); + virtual bool WriteData(const unsigned char *buf, unsigned int length, uint64_t pts = 0); virtual bool Poll(void); virtual void Reset(void); @@ -53,9 +53,8 @@ private: bool m_ready; uint64_t m_pts; - cMutex *m_mutex; - cCondWait *m_wait; - cAudioParser *m_parser; + cCondWait *m_wait; + cAudioParser *m_parser; cOmx *m_omx; }; diff --git a/display.c b/display.c new file mode 100644 index 0000000..64b83e6 --- /dev/null +++ b/display.c @@ -0,0 +1,106 @@ +/* + * See the README file for copyright information and how to reach the author. + * + * $Id$ + */ + +#include "display.h" +#include "tools.h" + +#include <vdr/tools.h> + +#include "interface/vmcs_host/vc_dispmanx.h" + +#define ALIGN_UP(value, align) (((value) & (align-1)) ? \ + (((value) + (align-1)) & ~(align-1)) : (value)) + +DISPMANX_DISPLAY_HANDLE_T cRpiDisplay::s_display = 0; +DISPMANX_UPDATE_HANDLE_T cRpiDisplay::s_update = 0; + +int cRpiDisplay::Open(int device) +{ + s_display = vc_dispmanx_display_open(device); + if (!s_display) + { + ELOG("failed to open display!"); + return -1; + } + + s_update = vc_dispmanx_update_start(0); + if (s_update == DISPMANX_NO_HANDLE) + { + ELOG("failed to start display update!"); + Close(); + return -1; + } + return 0; +} + +void cRpiDisplay::Close(void) +{ + if (s_display) + vc_dispmanx_display_close(s_display); + + s_display = 0; +} + +int cRpiDisplay::GetSize(int &width, int &height) +{ + if (!s_display) + return -1; + + DISPMANX_MODEINFO_T info; + memset(&info, 0, sizeof(info)); + + if (vc_dispmanx_display_get_info(s_display, &info)) + { + ELOG("failed to get display info!"); + return -1; + } + + width = info.width; + height = info.height; + + return 0; +} + +int cRpiDisplay::AddElement(DISPMANX_ELEMENT_HANDLE_T &element, + int width, int height, int layer) +{ + if (!s_display) + return -1; + + VC_RECT_T dstRect = { 0, 0, width, height }; + VC_RECT_T srcRect = { 0, 0, width << 16, height << 16 }; + + element = vc_dispmanx_element_add(s_update, s_display, layer, &dstRect, 0, + &srcRect, DISPMANX_PROTECTION_NONE, 0, 0, (DISPMANX_TRANSFORM_T)0); + + if (!element) + { + ELOG("failed to add display element!"); + return -1; + } + + vc_dispmanx_update_submit_sync(s_update); + return 0; +} + +int cRpiDisplay::Snapshot(uint8_t* frame, int width, int height) +{ + if (!s_display) + return -1; + + uint32_t image; + DISPMANX_RESOURCE_HANDLE_T res = vc_dispmanx_resource_create( + VC_IMAGE_RGB888, width, height, &image); + + vc_dispmanx_snapshot(s_display, res, + (DISPMANX_TRANSFORM_T)(DISPMANX_SNAPSHOT_PACK)); + + VC_RECT_T rect = { 0, 0, width, height }; + vc_dispmanx_resource_read_data(res, &rect, frame, width * 3); + + vc_dispmanx_resource_delete(res); + return 0; +} diff --git a/display.h b/display.h new file mode 100644 index 0000000..bf608c9 --- /dev/null +++ b/display.h @@ -0,0 +1,32 @@ +/* + * See the README file for copyright information and how to reach the author. + * + * $Id$ + */ + +#ifndef DISPLAY_H +#define DISPLAY_H + +#include "interface/vmcs_host/vc_dispmanx_types.h" + +class cRpiDisplay +{ + +public: + + static int Open(int device); + static void Close(void); + + static int GetSize(int &width, int &height); + static int AddElement(DISPMANX_ELEMENT_HANDLE_T &element, + int width, int height, int layer); + static int Snapshot(uint8_t* frame, int width, int height); + +private: + + static DISPMANX_DISPLAY_HANDLE_T s_display; + static DISPMANX_UPDATE_HANDLE_T s_update; + +}; + +#endif @@ -17,12 +17,12 @@ 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 + 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) { \ @@ -104,26 +104,32 @@ void cOmx::Action(void) { while (Running()) { - m_portEventReady->Wait(); + m_eventReady->Wait(); while (!m_portEvents->empty()) { HandlePortSettingsChanged(m_portEvents->front()); m_portEvents->pop(); } + if (m_stallEvent) + { + if (IsBufferStall() && m_onBufferStall) + m_onBufferStall(m_onBufferStallData); + m_stallEvent = false; + } } } void cOmx::HandlePortSettingsChanged(unsigned int portId) { -// dsyslog("rpihddevice: HandlePortSettingsChanged(%d)", portId); + DBG("HandlePortSettingsChanged(%d)", portId); switch (portId) { case 191: if (ilclient_setup_tunnel(&m_tun[eVideoFxToVideoScheduler], 0, 0) != 0) - esyslog("rpihddevice: failed to setup up tunnel from video fx to scheduler!"); + ELOG("failed to setup up tunnel from video fx to scheduler!"); if (ilclient_change_component_state(m_comp[eVideoScheduler], OMX_StateExecuting) != 0) - esyslog("rpihddevice: failed to enable video scheduler!"); + ELOG("failed to enable video scheduler!"); break; case 131: @@ -132,46 +138,51 @@ void cOmx::HandlePortSettingsChanged(unsigned int portId) 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!"); + ELOG("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!"); + ELOG("failed to get video decoder interlace config!"); + + m_videoWidth = portdef.format.video.nFrameWidth; + m_videoHeight = portdef.format.video.nFrameHeight; + m_videoInterlaced = interlace.eMode != OMX_InterlaceProgressive; bool deinterlace; - deinterlace = (cRpiSetup::IsDisplayProgressive() && - (interlace.eMode != OMX_InterlaceProgressive)); + deinterlace = (cRpiSetup::IsDisplayProgressive() && m_videoInterlaced); - dsyslog("rpihddevice: decoding video %dx%d%s, %sabling deinterlacer", - portdef.format.video.nFrameWidth, - portdef.format.video.nFrameHeight, - interlace.eMode == OMX_InterlaceProgressive ? "p" : "i", + ILOG("decoding video %dx%d%s, %sabling deinterlacer", + m_videoWidth, m_videoHeight, m_videoInterlaced ? "i" : "p", deinterlace ? "en" : "dis"); OMX_CONFIG_IMAGEFILTERPARAMSTYPE filterparam; OMX_INIT_STRUCT(filterparam); filterparam.nPortIndex = 191; - filterparam.nNumParams = 1; - filterparam.nParams[0] = 3; - filterparam.eImageFilter = deinterlace ? OMX_ImageFilterDeInterlaceAdvanced : OMX_ImageFilterNone; + filterparam.eImageFilter = OMX_ImageFilterNone; + if (deinterlace) + { + filterparam.nNumParams = 1; + filterparam.nParams[0] = 3; + filterparam.eImageFilter = OMX_ImageFilterDeInterlaceAdvanced; + } if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eVideoFx]), OMX_IndexConfigCommonImageFilterParameters, &filterparam) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set deinterlacing paramaters!"); + ELOG("failed to set deinterlacing paramaters!"); if (ilclient_setup_tunnel(&m_tun[eVideoDecoderToVideoFx], 0, 0) != 0) - esyslog("rpihddevice: failed to setup up tunnel from video decoder to fx!"); + ELOG("failed to setup up tunnel from video decoder to fx!"); if (ilclient_change_component_state(m_comp[eVideoFx], OMX_StateExecuting) != 0) - esyslog("rpihddevice: failed to enable video fx!"); + ELOG("failed to enable video fx!"); 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_setup_tunnel(&m_tun[eVideoSchedulerToVideoRender], 0, 0) != 0) + ELOG("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!"); + ELOG("failed to enable video render!"); break; } } @@ -189,38 +200,54 @@ void cOmx::OnPortSettingsChanged(void *instance, COMPONENT_T *comp, OMX_U32 data { cOmx* omx = static_cast <cOmx*> (instance); omx->m_portEvents->push(data); - omx->m_portEventReady->Signal(); + omx->m_eventReady->Signal(); } void cOmx::OnEndOfStream(void *instance, COMPONENT_T *comp, OMX_U32 data) { - cOmx* omx = static_cast <cOmx*> (instance); + //cOmx* omx = static_cast <cOmx*> (instance); } void cOmx::OnError(void *instance, COMPONENT_T *comp, OMX_U32 data) { if ((OMX_S32)data != OMX_ErrorSameState) - esyslog("rpihddevice: OmxError(%s)", errStr((int)data)); + ELOG("OmxError(%s)", errStr((int)data)); +} + +void cOmx::OnConfigChanged(void *instance, COMPONENT_T *comp, OMX_U32 data) +{ + cOmx* omx = static_cast <cOmx*> (instance); + if (data == OMX_IndexConfigBufferStall) + { + omx->m_stallEvent = true; + omx->m_eventReady->Signal(); + } } cOmx::cOmx() : cThread(), - m_mutex(new cMutex()), + m_videoWidth(0), + m_videoHeight(0), + m_videoInterlaced(false), m_setAudioStartTime(false), m_setVideoStartTime(false), m_setVideoDiscontinuity(false), m_freeAudioBuffers(0), m_freeVideoBuffers(0), - m_clockReference(eClockRefAudio), - m_portEventReady(new cCondWait()), - m_portEvents(new std::queue<unsigned int>) + m_clockReference(eClockRefNone), + m_eventReady(new cCondWait()), + m_portEvents(new std::queue<unsigned int>), + m_stallEvent(false), + m_onBufferStall(0), + m_onBufferStallData(0) { + memset(m_tun, 0, sizeof(m_tun)); + memset(m_comp, 0, sizeof(m_comp)); } cOmx::~cOmx() { - delete m_mutex; - delete m_portEventReady; + delete m_eventReady; delete m_portEvents; } @@ -228,47 +255,48 @@ int cOmx::Init(void) { m_client = ilclient_init(); if (m_client == NULL) - esyslog("rpihddevice: ilclient_init() failed!"); + ELOG("ilclient_init() failed!"); if (OMX_Init() != OMX_ErrorNone) - esyslog("rpihddevice: OMX_Init() failed!"); + ELOG("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); + ilclient_set_configchanged_callback(m_client, OnConfigChanged, 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!"); + ELOG("failed creating video decoder!"); // create image_fx if (ilclient_create_component(m_client, &m_comp[eVideoFx], "image_fx", ILCLIENT_DISABLE_ALL_PORTS) != 0) - esyslog("rpihddevice: failed creating video fx!"); + ELOG("failed creating video fx!"); // 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!"); + ELOG("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!"); + ELOG("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!"); + ELOG("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!"); + ELOG("failed creating video scheduler!"); // setup tunnels set_tunnel(&m_tun[eVideoDecoderToVideoFx], @@ -288,62 +316,45 @@ int cOmx::Init(void) // 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!"); + ELOG("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!"); + ELOG("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); ilclient_change_component_state(m_comp[eVideoFx], OMX_StateIdle); + ilclient_change_component_state(m_comp[eAudioRender], OMX_StateIdle); // set up the number and size of buffers for audio render - m_freeAudioBuffers = 2; //64; + m_freeAudioBuffers = 8; 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; + ELOG("failed to get audio render port parameters!"); + param.nBufferSize = KILOBYTE(160); 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!"); + ELOG("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!"); + ELOG("failed to get video decoder port parameters!"); m_freeVideoBuffers = param.nBufferCountActual; - dsyslog("rpihddevice: started with %d video and %d audio buffers", + DBG("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!"); + SetBufferStall(1500); - 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!"); - - SetClockState(cOmx::eClockStateRun); + FlushVideo(); + FlushAudio(); Start(); @@ -360,11 +371,18 @@ int cOmx::DeInit(void) ilclient_cleanup_components(m_comp); OMX_Deinit(); + ilclient_destroy(m_client); return 0; } +void cOmx::SetBufferStallCallback(void (*onBufferStall)(void*), void* data) +{ + m_onBufferStall = onBufferStall; + m_onBufferStallData = data; +} + OMX_TICKS cOmx::ToOmxTicks(int64_t val) { OMX_TICKS ticks; @@ -399,20 +417,16 @@ uint64_t cOmx::TicksToPts(OMX_TICKS &ticks) 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!"); + ELOG("failed get current clock reference!"); else stc = TicksToPts(timestamp.nTimestamp); -// dsyslog("S %u", timestamp.nTimestamp.nLowPart); - return stc; } @@ -423,7 +437,7 @@ bool cOmx::IsClockRunning(void) if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eClock]), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) - esyslog("rpihddevice: failed get clock state!"); + ELOG("failed get clock state!"); if (cstate.eState == OMX_TIME_ClockStateRunning) return true; @@ -433,20 +447,20 @@ bool cOmx::IsClockRunning(void) void cOmx::SetClockState(eClockState clockState) { -/* dsyslog("rpihddevice: SetClockState(%s)", + DBG("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!"); + ELOG("failed to get clock state!"); // if clock is already running, we need to stop it first if ((cstate.eState == OMX_TIME_ClockStateRunning) && @@ -457,7 +471,7 @@ void cOmx::SetClockState(eClockState clockState) 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!"); + ELOG("failed to stop clock!"); } cstate.nWaitMask = 0; @@ -492,13 +506,13 @@ void cOmx::SetClockState(eClockState clockState) break; } - if (cstate.eState == OMX_TIME_ClockStateWaitingForStartTime) - // 200ms pre roll, value taken from omxplayer - cstate.nOffset = ToOmxTicks(-1000LL * 200); +// if (cstate.eState == OMX_TIME_ClockStateWaitingForStartTime) + // 50ms pre roll, was 200ms at omxplayer +// cstate.nOffset = ToOmxTicks(-1000LL * 50); if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]), OMX_IndexConfigTimeClockState, &cstate) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set clock state!"); + ELOG("failed to set clock state!"); } void cOmx::SetClockScale(float scale) @@ -509,7 +523,7 @@ void cOmx::SetClockScale(float scale) if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]), OMX_IndexConfigTimeScale, &scaleType) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set clock scale (%d)!", scaleType.xScale); + ELOG("failed to set clock scale (%d)!", scaleType.xScale); } void cOmx::SetStartTime(uint64_t pts) @@ -521,7 +535,7 @@ void cOmx::SetStartTime(uint64_t pts) if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]), OMX_IndexConfigTimeClientStartTime, &timeStamp) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set current start time!"); + ELOG("failed to set current start time!"); } void cOmx::SetCurrentReferenceTime(uint64_t pts) @@ -535,7 +549,7 @@ void cOmx::SetCurrentReferenceTime(uint64_t pts) m_clockReference == eClockRefAudio ? OMX_IndexConfigTimeCurrentAudioReference : OMX_IndexConfigTimeCurrentVideoReference, &timeStamp) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set current %s reference time!", + ELOG("failed to set current %s reference time!", m_clockReference == eClockRefAudio ? "audio" : "video"); } @@ -549,7 +563,7 @@ unsigned int cOmx::GetMediaTime(void) if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eClock]), OMX_IndexConfigTimeCurrentMediaTime, ×tamp) != OMX_ErrorNone) - esyslog("rpihddevice: failed get current clock reference!"); + ELOG("failed get current clock reference!"); else ret = timestamp.nTimestamp.nLowPart; @@ -558,19 +572,61 @@ unsigned int cOmx::GetMediaTime(void) void cOmx::SetClockReference(eClockReference clockReference) { - m_clockReference = clockReference; + if (m_clockReference != clockReference) + { + OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE refClock; + OMX_INIT_STRUCT(refClock); + refClock.eClock = + (clockReference == eClockRefAudio) ? OMX_TIME_RefClockAudio : + (clockReference == eClockRefVideo) ? OMX_TIME_RefClockVideo : + OMX_TIME_RefClockNone; - 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) + ELOG("failed set active clock reference!"); + else + DBG("set active clock reference to %s", + clockReference == eClockRefAudio ? "audio" : + clockReference == eClockRefVideo ? "video" : "none"); + + m_clockReference = clockReference; + } +} - if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eClock]), - OMX_IndexConfigTimeActiveRefClock, &refClock) != OMX_ErrorNone) - esyslog("rpihddevice: failed set active clock reference!"); -/* else - dsyslog("rpihddevice: set active clock reference to %s", - m_clockReference == eClockRefAudio ? "audio" : "video"); */ +void cOmx::SetBufferStall(int delayMs) +{ + if (delayMs > 0) + { + OMX_CONFIG_BUFFERSTALLTYPE stallConf; + OMX_INIT_STRUCT(stallConf); + stallConf.nPortIndex = 131; + stallConf.nDelay = delayMs * 1000; + if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eVideoDecoder]), + OMX_IndexConfigBufferStall, &stallConf) != OMX_ErrorNone) + ELOG("failed to set video decoder stall config!"); + } + + // set buffer stall call back + OMX_CONFIG_REQUESTCALLBACKTYPE reqCallback; + OMX_INIT_STRUCT(reqCallback); + reqCallback.nPortIndex = 131; + reqCallback.nIndex = OMX_IndexConfigBufferStall; + reqCallback.bEnable = delayMs > 0 ? OMX_TRUE : OMX_FALSE; + if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eVideoDecoder]), + OMX_IndexConfigRequestCallback, &reqCallback) != OMX_ErrorNone) + ELOG("failed to set video decoder stall call back!"); +} + +bool cOmx::IsBufferStall(void) +{ + OMX_CONFIG_BUFFERSTALLTYPE stallConf; + OMX_INIT_STRUCT(stallConf); + stallConf.nPortIndex = 131; + if (OMX_GetConfig(ILC_GET_HANDLE(m_comp[eVideoDecoder]), + OMX_IndexConfigBufferStall, &stallConf) != OMX_ErrorNone) + ELOG("failed to get video decoder stall config!"); + + return stallConf.bStalled == OMX_TRUE; } void cOmx::SetVolume(int vol) @@ -583,7 +639,7 @@ void cOmx::SetVolume(int vol) if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eAudioRender]), OMX_IndexConfigAudioVolume, &volume) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set volume!"); + ELOG("failed to set volume!"); } void cOmx::SetMute(bool mute) @@ -595,7 +651,7 @@ void cOmx::SetMute(bool mute) if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eAudioRender]), OMX_IndexConfigAudioMute, &amute) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set mute state!"); + ELOG("failed to set mute state!"); } void cOmx::SendEos(void) @@ -609,17 +665,15 @@ void cOmx::SendEos(void) 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!"); + ELOG("failed to send empty packet to video decoder!"); if (!m_eosEvent->Wait(10000)) - esyslog("rpihddevice: time out waiting for EOS event!"); + ELOG("time out waiting for EOS event!"); #endif } void cOmx::StopVideo(void) { - dsyslog("rpihddevice: StopVideo()"); - // put video decoder into idle ilclient_change_component_state(m_comp[eVideoDecoder], OMX_StateIdle); @@ -642,13 +696,17 @@ void cOmx::StopVideo(void) // disable port buffers and allow video decoder to reconfig ilclient_disable_port_buffers(m_comp[eVideoDecoder], 130, NULL, NULL, NULL); + + m_videoWidth = 0; + m_videoHeight = 0; + m_videoInterlaced = false; } void cOmx::StopAudio(void) { // put audio render onto idle ilclient_flush_tunnels(&m_tun[eClockToAudioRender], 1); -// ilclient_disable_tunnel(&m_tun[eClockToAudioRender]); + 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); } @@ -665,7 +723,7 @@ void cOmx::SetVideoDataUnitType(eDataUnitType dataUnitType) if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]), OMX_IndexParamBrcmDataUnit, &dataUnit) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set video decoder data unit type!"); + ELOG("failed to set video decoder data unit type!"); } @@ -676,7 +734,7 @@ void cOmx::SetVideoErrorConcealment(bool startWithValidFrame) ectype.bStartWithValidFrame = startWithValidFrame ? OMX_TRUE : 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"); + ELOG("failed to set video decode error concealment failed\n"); } void cOmx::FlushAudio(void) @@ -684,7 +742,7 @@ 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!"); + ELOG("failed to flush audio render!"); } void cOmx::FlushVideo(bool flushRender) @@ -692,7 +750,7 @@ 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!"); + ELOG("failed to flush video decoder!"); ilclient_flush_tunnels(&m_tun[eVideoDecoderToVideoFx], 1); ilclient_flush_tunnels(&m_tun[eVideoFxToVideoScheduler], 1); @@ -706,7 +764,7 @@ void cOmx::FlushVideo(bool flushRender) int cOmx::SetVideoCodec(cVideoCodec::eCodec codec, eDataUnitType dataUnit) { if (ilclient_change_component_state(m_comp[eVideoDecoder], OMX_StateIdle) != 0) - esyslog("rpihddevice: failed to set video decoder to idle state!"); + ELOG("failed to set video decoder to idle state!"); // configure video decoder OMX_VIDEO_PARAM_PORTFORMATTYPE videoFormat; @@ -719,7 +777,7 @@ int cOmx::SetVideoCodec(cVideoCodec::eCodec codec, eDataUnitType dataUnit) if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]), OMX_IndexParamVideoPortFormat, &videoFormat) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set video decoder parameters!"); + ELOG("failed to set video decoder parameters!"); // start with valid frames only if codec is MPEG2 SetVideoErrorConcealment(codec == cVideoCodec::eMPEG2); @@ -727,14 +785,14 @@ int cOmx::SetVideoCodec(cVideoCodec::eCodec codec, eDataUnitType dataUnit) SetVideoDecoderExtraBuffers(3); if (ilclient_enable_port_buffers(m_comp[eVideoDecoder], 130, NULL, NULL, NULL) != 0) - esyslog("rpihddevice: failed to enable port buffer on video decoder!"); + ELOG("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!"); + ELOG("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!"); + ELOG("failed to setup up tunnel from clock to video scheduler!"); return 0; } @@ -747,27 +805,18 @@ void cOmx::SetVideoDecoderExtraBuffers(int extraBuffers) u32.nU32 = extraBuffers; if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eVideoDecoder]), OMX_IndexParamBrcmExtraBuffers, &u32) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set video decoder extra buffers!"); + ELOG("failed to set video decoder extra buffers!"); } int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels, cAudioPort::ePort audioPort, int samplingRate) { - // 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!"); + ELOG("failed to get audio port format parameters!"); format.eEncoding = outputFormat == cAudioCodec::ePCM ? OMX_AUDIO_CodingPCM : @@ -775,12 +824,12 @@ int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels, outputFormat == cAudioCodec::eAC3 ? OMX_AUDIO_CodingDDP : outputFormat == cAudioCodec::eEAC3 ? OMX_AUDIO_CodingDDP : outputFormat == cAudioCodec::eAAC ? OMX_AUDIO_CodingAAC : - outputFormat == cAudioCodec::eADTS ? OMX_AUDIO_CodingAAC : + outputFormat == cAudioCodec::eAAC ? OMX_AUDIO_CodingAAC : 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!"); + ELOG("failed to set audio port format parameters!"); switch (outputFormat) { @@ -795,7 +844,7 @@ int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels, if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), OMX_IndexParamAudioMp3, &mp3) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set audio render mp3 parameters!"); + ELOG("failed to set audio render mp3 parameters!"); break; case cAudioCodec::eAC3: @@ -809,24 +858,20 @@ int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels, if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), OMX_IndexParamAudioDdp, &ddp) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set audio render ddp parameters!"); + ELOG("failed to set audio render ddp parameters!"); break; case cAudioCodec::eAAC: - case cAudioCodec::eADTS: OMX_AUDIO_PARAM_AACPROFILETYPE aac; OMX_INIT_STRUCT(aac); aac.nPortIndex = 100; aac.nChannels = channels; aac.nSampleRate = samplingRate; - aac.eAACStreamFormat = - outputFormat == cAudioCodec::eAAC ? OMX_AUDIO_AACStreamFormatMP4LATM : - outputFormat == cAudioCodec::eADTS ? OMX_AUDIO_AACStreamFormatMP4ADTS : - (OMX_AUDIO_AACSTREAMFORMATTYPE)0; + aac.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS; if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), OMX_IndexParamAudioAac, &aac) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set audio render aac parameters!"); + ELOG("failed to set audio render aac parameters!"); break; case cAudioCodec::ePCM: @@ -844,11 +889,11 @@ int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels, if (OMX_SetParameter(ILC_GET_HANDLE(m_comp[eAudioRender]), OMX_IndexParamAudioPcm, &pcm) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set audio render pcm parameters!"); + ELOG("failed to set audio render pcm parameters!"); break; default: - esyslog("rpihddevice: output codec not supported: %s!", + ELOG("output codec not supported: %s!", cAudioCodec::Str(outputFormat)); break; } @@ -860,22 +905,65 @@ int cOmx::SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels, if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eAudioRender]), OMX_IndexConfigBrcmAudioDestination, &audioDest) != OMX_ErrorNone) - esyslog("rpihddevice: failed to set audio destination!"); + ELOG("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!"); + ELOG("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!"); + ELOG("failed to setup up tunnel from clock to video scheduler!"); return 0; } +void cOmx::GetVideoSize(int &width, int &height, bool &interlaced) +{ + width = m_videoWidth; + height = m_videoHeight; + interlaced = m_videoInterlaced; +} + +void cOmx::SetDisplayMode(bool letterbox, bool noaspect) +{ + OMX_CONFIG_DISPLAYREGIONTYPE region; + OMX_INIT_STRUCT(region); + region.nPortIndex = 90; + region.set = (OMX_DISPLAYSETTYPE) + (OMX_DISPLAY_SET_MODE | OMX_DISPLAY_SET_NOASPECT); + + region.noaspect = noaspect ? OMX_TRUE : OMX_FALSE; + region.mode = letterbox ? + OMX_DISPLAY_MODE_LETTERBOX : OMX_DISPLAY_MODE_FILL; + + if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eVideoRender]), + OMX_IndexConfigDisplayRegion, ®ion) != OMX_ErrorNone) + ELOG("failed to set display region!"); +} + +void cOmx::SetDisplayRegion(int x, int y, int width, int height) +{ + OMX_CONFIG_DISPLAYREGIONTYPE region; + OMX_INIT_STRUCT(region); + region.nPortIndex = 90; + region.set = (OMX_DISPLAYSETTYPE) + (OMX_DISPLAY_SET_FULLSCREEN | OMX_DISPLAY_SET_DEST_RECT); + + region.fullscreen = (!x && !y && !width && !height) ? OMX_TRUE : OMX_FALSE; + region.dest_rect.x_offset = x; + region.dest_rect.y_offset = y; + region.dest_rect.width = width; + region.dest_rect.height = height; + + if (OMX_SetConfig(ILC_GET_HANDLE(m_comp[eVideoRender]), + OMX_IndexConfigDisplayRegion, ®ion) != OMX_ErrorNone) + ELOG("failed to set display region!"); +} + OMX_BUFFERHEADERTYPE* cOmx::GetAudioBuffer(uint64_t pts) { - m_mutex->Lock(); + Lock(); OMX_BUFFERHEADERTYPE* buf = NULL; if (m_freeAudioBuffers > 0) @@ -891,13 +979,14 @@ OMX_BUFFERHEADERTYPE* cOmx::GetAudioBuffer(uint64_t pts) m_freeAudioBuffers--; } } - m_mutex->Unlock(); + + Unlock(); return buf; } OMX_BUFFERHEADERTYPE* cOmx::GetVideoBuffer(uint64_t pts) { - m_mutex->Lock(); + Lock(); OMX_BUFFERHEADERTYPE* buf = NULL; if (m_freeVideoBuffers > 0) @@ -915,7 +1004,8 @@ OMX_BUFFERHEADERTYPE* cOmx::GetVideoBuffer(uint64_t pts) m_freeVideoBuffers--; } } - m_mutex->Unlock(); + + Unlock(); return buf; } @@ -9,7 +9,7 @@ #include <queue> #include <vdr/thread.h> -#include "types.h" +#include "tools.h" extern "C" { @@ -26,6 +26,8 @@ public: int Init(void); int DeInit(void); + void SetBufferStallCallback(void (*onBufferStall)(void*), void* data); + static OMX_TICKS ToOmxTicks(int64_t val); static int64_t FromOmxTicks(OMX_TICKS &ticks); static void PtsToTicks(uint64_t pts, OMX_TICKS &ticks); @@ -50,7 +52,8 @@ public: enum eClockReference { eClockRefAudio, - eClockRefVideo + eClockRefVideo, + eClockRefNone }; void SetClockReference(eClockReference clockReference); @@ -76,6 +79,10 @@ public: eDataUnitType dataUnit = eArbitraryStreamSection); int SetupAudioRender(cAudioCodec::eCodec outputFormat, int channels, cAudioPort::ePort audioPort, int samplingRate = 0); + void GetVideoSize(int &width, int &height, bool &interlaced); + + void SetDisplayMode(bool letterbox, bool noaspect); + void SetDisplayRegion(int x, int y, int width, int height); OMX_BUFFERHEADERTYPE* GetAudioBuffer(uint64_t pts = 0); OMX_BUFFERHEADERTYPE* GetVideoBuffer(uint64_t pts = 0); @@ -114,7 +121,9 @@ private: COMPONENT_T *m_comp[cOmx::eNumComponents + 1]; TUNNEL_T m_tun[cOmx::eNumTunnels + 1]; - cMutex *m_mutex; + int m_videoWidth; + int m_videoHeight; + bool m_videoInterlaced; bool m_setAudioStartTime; bool m_setVideoStartTime; @@ -125,14 +134,21 @@ private: eClockReference m_clockReference; - cCondWait *m_portEventReady; - std::queue<unsigned int> *m_portEvents; + cCondWait *m_eventReady; + std::queue<unsigned int> *m_portEvents; + bool m_stallEvent; + void (*m_onBufferStall)(void*); + void *m_onBufferStallData; + void HandlePortSettingsChanged(unsigned int portId); + void SetBufferStall(int delayMs); + bool IsBufferStall(void); static void OnBufferEmpty(void *instance, COMPONENT_T *comp); static void OnPortSettingsChanged(void *instance, COMPONENT_T *comp, OMX_U32 data); static void OnEndOfStream(void *instance, COMPONENT_T *comp, OMX_U32 data); static void OnError(void *instance, COMPONENT_T *comp, OMX_U32 data); + static void OnConfigChanged(void *instance, COMPONENT_T *comp, OMX_U32 data); }; diff --git a/omxdevice.c b/omxdevice.c index a87bd87..f80a1e2 100644 --- a/omxdevice.c +++ b/omxdevice.c @@ -7,6 +7,7 @@ #include "omxdevice.h" #include "omx.h" #include "audio.h" +#include "display.h" #include "setup.h" #include <vdr/thread.h> @@ -35,6 +36,8 @@ cOmxDevice::cOmxDevice(void (*onPrimaryDevice)(void)) : cOmxDevice::~cOmxDevice() { + DeInit(); + delete m_omx; delete m_audio; delete m_mutex; @@ -44,14 +47,15 @@ int cOmxDevice::Init(void) { if (m_omx->Init() < 0) { - esyslog("rpihddevice: failed to initialize OMX!"); + ELOG("failed to initialize OMX!"); return -1; } if (m_audio->Init() < 0) { - esyslog("rpihddevice: failed to initialize audio!"); + ELOG("failed to initialize audio!"); return -1; } + m_omx->SetBufferStallCallback(&OnBufferStall, this); return 0; } @@ -59,12 +63,12 @@ int cOmxDevice::DeInit(void) { if (m_audio->DeInit() < 0) { - esyslog("rpihddevice: failed to deinitialize audio!"); + ELOG("failed to deinitialize audio!"); return -1; } if (m_omx->DeInit() < 0) { - esyslog("rpihddevice: failed to deinitialize OMX!"); + ELOG("failed to deinitialize OMX!"); return -1; } return 0; @@ -75,10 +79,39 @@ void cOmxDevice::GetOsdSize(int &Width, int &Height, double &PixelAspect) cRpiSetup::GetDisplaySize(Width, Height, PixelAspect); } +void cOmxDevice::GetVideoSize(int &Width, int &Height, double &VideoAspect) +{ + bool interlaced; + m_omx->GetVideoSize(Width, Height, interlaced); + + if (Height) + VideoAspect = (double)Width / Height; +} + +void cOmxDevice::SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat) +{ + DBG("SetVideoDisplayFormat(%s)", + VideoDisplayFormat == vdfPanAndScan ? "PanAndScan" : + VideoDisplayFormat == vdfLetterBox ? "LetterBox" : + VideoDisplayFormat == vdfCenterCutOut ? "CenterCutOut" : "undefined"); + + m_omx->SetDisplayMode(VideoDisplayFormat == vdfLetterBox, false); + + cDevice::SetVideoDisplayFormat(VideoDisplayFormat); +} + +void cOmxDevice::ScaleVideo(const cRect &Rect) +{ + DBG("ScaleVideo(%d, %d, %d, %d)", + Rect.X(), Rect.Y(), Rect.Width(), Rect.Height()); + + m_omx->SetDisplayRegion(Rect.X(), Rect.Y(), Rect.Width(), Rect.Height()); +} + bool cOmxDevice::SetPlayMode(ePlayMode PlayMode) { m_mutex->Lock(); - dsyslog("rpihddevice: SetPlayMode(%s)", + DBG("SetPlayMode(%s)", PlayMode == pmNone ? "none" : PlayMode == pmAudioVideo ? "Audio/Video" : PlayMode == pmAudioOnly ? "Audio only" : @@ -120,9 +153,11 @@ void cOmxDevice::StillPicture(const uchar *Data, int Length) cDevice::StillPicture(Data, Length); else { + Clear(); //? // to get a picture displayed, PlayVideo() needs to be called - // twice for MPEG2 and 10x for H264... ? - int repeat = ParseVideoCodec(Data, Length) == cVideoCodec::eMPEG2 ? 2 : 10; + // 4x for MPEG2 and 12x for H264... ? + int repeat = + ParseVideoCodec(Data, Length) == cVideoCodec::eMPEG2 ? 4 : 12; while (repeat--) PlayVideo(Data, Length, true); @@ -135,14 +170,17 @@ int cOmxDevice::PlayAudio(const uchar *Data, int Length, uchar Id) return Length; m_mutex->Lock(); - int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : 0; if (!m_hasAudio) { - m_omx->SetClockReference(cOmx::eClockRefAudio); + // start clock once an audio packet is played, even + // if it's been set to wait state before + m_omx->SetClockState(cOmx::eClockStateRun); m_hasAudio = true; } + int64_t pts = PesHasPts(Data) ? PesGetPts(Data) : 0; + // keep track of direction in case of trick speed if (m_trickRequest && pts) { @@ -153,7 +191,7 @@ int cOmxDevice::PlayAudio(const uchar *Data, int Length, uchar Id) } int ret = m_audio->WriteData(Data + PesPayloadOffset(Data), - Length - PesPayloadOffset(Data), pts); + Length - PesPayloadOffset(Data), pts) ? Length : 0; m_mutex->Unlock(); return ret; @@ -186,8 +224,7 @@ int cOmxDevice::PlayVideo(const uchar *Data, int Length, bool singleFrame) { videoRestart = true; m_omx->SetVideoCodec(codec, cOmx::eArbitraryStreamSection); - dsyslog("rpihddevice: set video codec to %s", - cVideoCodec::Str(codec)); + DLOG("set video codec to %s", cVideoCodec::Str(codec)); } else Skins.QueueMessage(mtError, tr("video format not supported!")); @@ -195,14 +232,12 @@ int cOmxDevice::PlayVideo(const uchar *Data, int Length, bool singleFrame) if (videoRestart) { - m_hasVideo = true; - + // put clock in waiting state. that's only needed for video only + // play back, since audio will start clock directly if (!m_hasAudio) - { - m_omx->SetClockReference(cOmx::eClockRefVideo); - m_omx->SetCurrentReferenceTime(0); m_omx->SetClockState(cOmx::eClockStateWaitForVideo); - } + + m_hasVideo = true; } // keep track of direction in case of trick speed @@ -218,17 +253,12 @@ int cOmxDevice::PlayVideo(const uchar *Data, int Length, bool singleFrame) { while (Length) { - const uchar *payload = Data + PesPayloadOffset(Data); - unsigned int length = PesLength(Data) - PesPayloadOffset(Data); - - if (length > Length) - esyslog("rpihddevice: PES-Length > Length!"); - OMX_BUFFERHEADERTYPE *buf = m_omx->GetVideoBuffer(pts); if (buf) { - buf->nFilledLen = length; - memcpy(buf->pBuffer, payload, length); + buf->nFilledLen = PesLength(Data) - PesPayloadOffset(Data); + memcpy(buf->pBuffer, Data + PesPayloadOffset(Data), + PesLength(Data) - PesPayloadOffset(Data)); if (singleFrame && Length == PesLength(Data)) buf->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME; @@ -236,7 +266,7 @@ int cOmxDevice::PlayVideo(const uchar *Data, int Length, bool singleFrame) if (!m_omx->EmptyVideoBuffer(buf)) { ret = 0; - esyslog("rpihddevice: failed to pass buffer to video decoder!"); + ELOG("failed to pass buffer to video decoder!"); break; } } @@ -245,8 +275,10 @@ int cOmxDevice::PlayVideo(const uchar *Data, int Length, bool singleFrame) ret = 0; break; } + Length -= PesLength(Data); Data += PesLength(Data); + pts = PesHasPts(Data) ? PesGetPts(Data) : 0; } } @@ -259,19 +291,73 @@ int64_t cOmxDevice::GetSTC(void) return m_omx->GetSTC(); } -void cOmxDevice::Play(void) +uchar *cOmxDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY) { - m_mutex->Lock(); + DBG("GrabImage(%s, %dx%d)", Jpeg ? "JPEG" : "PNM", SizeX, SizeY); + + uint8_t* ret = NULL; + int width, height; + cRpiDisplay::GetSize(width, height); + + SizeX = (SizeX > 0) ? SizeX : width; + SizeY = (SizeY > 0) ? SizeY : height; + + // bigger than needed, but uint32_t ensures proper alignment + uint8_t* frame = (uint8_t*)(MALLOC(uint32_t, SizeX * SizeY)); + + if (!frame) + { + ELOG("failed to allocate image buffer!"); + return ret; + } + + if (cRpiDisplay::Snapshot(frame, SizeX, SizeY)) + { + ELOG("failed to grab image!"); + free(frame); + return ret; + } + + if (Jpeg) + ret = RgbToJpeg(frame, SizeX, SizeY, Size, Quality); + else + { + char buf[32]; + snprintf(buf, sizeof(buf), "P6\n%d\n%d\n255\n", SizeX, SizeY); + int l = strlen(buf); + Size = l + SizeX * SizeY * 3; + ret = (uint8_t *)malloc(Size); + if (ret) + { + memcpy(ret, buf, l); + memcpy(ret + l, frame, SizeX * SizeY * 3); + } + } + free(frame); + return ret; +} +void cOmxDevice::Clear(void) +{ + DBG("Clear()"); + m_mutex->Lock(); ResetAudioVideo(); - m_omx->SetStartTime(0); + m_mutex->Unlock(); + cDevice::Clear(); +} +void cOmxDevice::Play(void) +{ + DBG("Play()"); + m_mutex->Lock(); + ResetAudioVideo(); m_mutex->Unlock(); cDevice::Play(); } void cOmxDevice::Freeze(void) { + DBG("Freeze()"); m_mutex->Lock(); m_omx->SetClockScale(0.0f); m_mutex->Unlock(); @@ -281,6 +367,7 @@ void cOmxDevice::Freeze(void) #if APIVERSNUM >= 20103 void cOmxDevice::TrickSpeed(int Speed, bool Forward) { + DBG("TrickSpeed(%d, %sward)", Speed, Forward ? "for" : "back"); m_mutex->Lock(); ApplyTrickSpeed(Speed, Forward); m_mutex->Unlock(); @@ -288,6 +375,7 @@ void cOmxDevice::TrickSpeed(int Speed, bool Forward) #else void cOmxDevice::TrickSpeed(int Speed) { + DBG("TrickSpeed(%d)", Speed); m_mutex->Lock(); m_audioPts = 0; m_videoPts = 0; @@ -324,11 +412,14 @@ void cOmxDevice::ApplyTrickSpeed(int trickSpeed, bool forward) m_omx->SetClockScale(scale); m_omx->SetClockReference(cOmx::eClockRefVideo); - m_trickRequest = 0; + if (m_hasAudio) + { + m_audio->Reset(); + m_omx->FlushAudio(); + } m_skipAudio = true; - dsyslog("rpihddevice: ApplyTrickSpeed(%.3f, %sward)", - scale, forward ? "for" : "back"); + DBG("ApplyTrickSpeed(%.3f, %sward)", scale, forward ? "for" : "back"); } @@ -340,33 +431,24 @@ void cOmxDevice::PtsTracker(int64_t ptsDiff) m_playDirection += 2; if (m_playDirection < -2 || m_playDirection > 3) + { ApplyTrickSpeed(m_trickRequest, m_playDirection > 0); + m_trickRequest = 0; + } } -bool cOmxDevice::Flush(int TimeoutMs) -{ - dsyslog("rpihddevice: Flush()"); - return true; -} - -void cOmxDevice::Clear(void) +void cOmxDevice::HandleBufferStall() { + ELOG("buffer stall!"); m_mutex->Lock(); - ResetAudioVideo(); - m_omx->SetStartTime(0); - m_mutex->Unlock(); - cDevice::Clear(); } void cOmxDevice::ResetAudioVideo(bool flushVideoRender) { - m_omx->SetClockScale(1.0f); - m_skipAudio = false; - m_trickRequest = 0; - m_audioPts = 0; - m_videoPts = 0; + if (m_hasVideo) + m_omx->FlushVideo(flushVideoRender); if (m_hasAudio) { @@ -374,8 +456,15 @@ void cOmxDevice::ResetAudioVideo(bool flushVideoRender) m_omx->FlushAudio(); } - if (m_hasVideo) - m_omx->FlushVideo(flushVideoRender); + m_omx->SetClockReference(cOmx::eClockRefVideo); + m_omx->SetClockScale(1.0f); + m_omx->SetStartTime(0); + m_omx->SetClockState(cOmx::eClockStateStop); + + m_skipAudio = false; + m_trickRequest = 0; + m_audioPts = 0; + m_videoPts = 0; m_hasAudio = false; m_hasVideo = false; @@ -384,6 +473,7 @@ void cOmxDevice::ResetAudioVideo(bool flushVideoRender) void cOmxDevice::SetVolumeDevice(int Volume) { + DBG("SetVolume(%d)", Volume); if (Volume) { m_omx->SetVolume(Volume); diff --git a/omxdevice.h b/omxdevice.h index d28543e..ac77823 100644 --- a/omxdevice.h +++ b/omxdevice.h @@ -9,7 +9,7 @@ #include <vdr/device.h> -#include "types.h" +#include "tools.h" class cOmx; class cAudioDecoder; @@ -26,10 +26,17 @@ public: virtual int Init(void); virtual int DeInit(void); - virtual bool HasDecoder(void) const { return true; }; - virtual bool CanReplay(void) const { return true; }; + virtual bool HasDecoder(void) const { return true; } + virtual bool CanReplay(void) const { return true; } + virtual bool HasIBPTrickSpeed(void) { return true; } virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect); + virtual void GetVideoSize(int &Width, int &Height, double &VideoAspect); + virtual void SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat); + + virtual cRect CanScaleVideo(const cRect &Rect, int Alignment = taCenter) + { return Rect; } + virtual void ScaleVideo(const cRect &Rect = cRect::Null); virtual bool SetPlayMode(ePlayMode PlayMode); @@ -43,7 +50,7 @@ public: virtual int64_t GetSTC(void); - virtual bool HasIBPTrickSpeed(void) { return true; } + virtual uchar *GrabImage(int &Size, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); #if APIVERSNUM >= 20103 virtual void TrickSpeed(int Speed, bool Forward); @@ -57,7 +64,6 @@ public: virtual void SetVolumeDevice(int Volume); - virtual bool Flush(int TimeoutMs = 0); virtual bool Poll(cPoller &Poller, int TimeoutMs = 0); protected: @@ -69,6 +75,10 @@ private: void (*m_onPrimaryDevice)(void); virtual cVideoCodec::eCodec ParseVideoCodec(const uchar *data, int length); + static void OnBufferStall(void *data) + { (static_cast <cOmxDevice*> (data))->HandleBufferStall(); } + + void HandleBufferStall(); void ResetAudioVideo(bool flushVideoRender = false); void ApplyTrickSpeed(int trickSpeed, bool forward = true); @@ -10,8 +10,33 @@ #include <GLES/gl.h> #include "ovgosd.h" +#include "display.h" #include "omxdevice.h" #include "setup.h" +#include "tools.h" + +class cOvgOsd : public cOsd +{ + +public: + + cOvgOsd(int Left, int Top, uint Level, cOvg *ovg); + virtual ~cOvgOsd(); + + virtual void Flush(void); + virtual eOsdError SetAreas(const tArea *Areas, int NumAreas); + +protected: + + virtual void SetActive(bool On); + +private: + + cOvg *m_ovg; + +}; + +/* ------------------------------------------------------------------------- */ class cOvg : public cThread { @@ -21,7 +46,6 @@ public: cThread(), m_do(0), m_done(0), - m_mutex(0), m_width(0), m_height(0), m_aspect(0), @@ -37,7 +61,6 @@ public: m_do = new cCondWait(); m_done = new cCondWait(); - m_mutex = new cMutex(); Start(); } @@ -46,10 +69,11 @@ public: { Cancel(-1); Clear(); + while (Active()) + cCondWait::SleepMs(50); delete m_do; delete m_done; - delete m_mutex; } void GetDisplaySize(int &width, int &height, double &aspect) @@ -61,7 +85,7 @@ public: void DrawPixmap(int x, int y, int w, int h, int d, const uint8_t *data) { - m_mutex->Lock(); + Lock(); m_pixmap = data; m_d = d; m_x = x; @@ -70,34 +94,34 @@ public: m_h = h; m_do->Signal(); m_done->Wait(); - m_mutex->Unlock(); + Unlock(); } void Clear() { - m_mutex->Lock(); + Lock(); m_clear = true; m_do->Signal(); m_done->Wait(); - m_mutex->Unlock(); + Unlock(); } protected: virtual void Action(void) { - dsyslog("rpihddevice: cOvg() thread started"); + DLOG("cOvg() thread started"); EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (display == EGL_NO_DISPLAY) - esyslog("rpihddevice: failed to get EGL display connection!"); + ELOG("failed to get EGL display connection!"); if (eglInitialize(display, NULL, NULL) == EGL_FALSE) - esyslog("rpihddevice: failed to init EGL display connection!"); + ELOG("failed to init EGL display connection!"); eglBindAPI(EGL_OPENVG_API); - const EGLint fbAttr[] = { + const EGLint attr[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, @@ -108,40 +132,20 @@ protected: }; EGLConfig config; - EGLint numConfig; + EGLint nConfig; // get an appropriate EGL frame buffer configuration - if (eglChooseConfig(display, fbAttr, &config, 1, &numConfig) == EGL_FALSE) - esyslog("rpihddevice: failed to get EGL frame buffer config!"); + if (eglChooseConfig(display, attr, &config, 1, &nConfig) == EGL_FALSE) + ELOG("failed to get EGL frame buffer config!"); // create an EGL rendering context EGLContext context = eglCreateContext(display, config, NULL, NULL); if (context == EGL_NO_CONTEXT) - esyslog("rpihddevice: failed to create EGL rendering context!"); - - DISPMANX_DISPLAY_HANDLE_T dispmanDisplay = vc_dispmanx_display_open(0 /* LCD */); - DISPMANX_UPDATE_HANDLE_T dispmanUpdate = vc_dispmanx_update_start(0); - - VC_RECT_T dstRect = { 0, 0, m_width, m_height }; - VC_RECT_T srcRect = { 0, 0, m_width << 16, m_height << 16 }; - - DISPMANX_ELEMENT_HANDLE_T dispmanElement = vc_dispmanx_element_add( - 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 - DISPMANX_RESOURCE_HANDLE_T bgRsc = vc_dispmanx_resource_create(VC_IMAGE_RGB565, 1, 1, &pBgImage); - vc_dispmanx_rect_set(&dstRect, 0, 0, 1, 1); - vc_dispmanx_resource_write_data(bgRsc, VC_IMAGE_RGB565, sizeof(bgImage), &bgImage, &dstRect); - vc_dispmanx_rect_set(&srcRect, 0, 0, 0, 0); - 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); + ELOG("failed to create EGL rendering context!"); + + cRpiDisplay::Open(0 /* LCD */); + DISPMANX_ELEMENT_HANDLE_T dispmanElement; + cRpiDisplay::AddElement(dispmanElement, m_width, m_height, 2); EGL_DISPMANX_WINDOW_T nativewindow; nativewindow.element = dispmanElement; @@ -153,13 +157,14 @@ protected: EGL_NONE }; - EGLSurface surface = eglCreateWindowSurface(display, config, &nativewindow, windowAttr); + EGLSurface surface = eglCreateWindowSurface(display, config, + &nativewindow, windowAttr); if (surface == EGL_NO_SURFACE) - esyslog("rpihddevice: failed to create EGL window surface!"); + ELOG("failed to create EGL window surface!"); // connect the context to the surface if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) - esyslog("rpihddevice: failed to connect context to surface!"); + ELOG("failed to connect context to surface!"); float color[4] = {0.0f, 0.0f, 0.0f, 0.0f}; vgSetfv(VG_CLEAR_COLOR, 4, color); @@ -175,7 +180,8 @@ protected: vgScale(1.0f, -1.0f); vgTranslate(0.0f, -m_height); - VGImage image = vgCreateImage(VG_sARGB_8888, m_width, m_height, VG_IMAGE_QUALITY_BETTER); + VGImage image = vgCreateImage(VG_sARGB_8888, m_width, m_height, + VG_IMAGE_QUALITY_BETTER); while (Running()) { @@ -183,7 +189,8 @@ protected: if (m_pixmap) { vgClearImage(image, m_x, m_y, m_w, m_h); - vgImageSubData(image, m_pixmap, m_d, VG_sARGB_8888, m_x, m_y, m_w, m_h); + vgImageSubData(image, m_pixmap, m_d, VG_sARGB_8888, + m_x, m_y, m_w, m_h); vgDrawImage(image); m_pixmap = 0; } @@ -204,19 +211,21 @@ protected: eglSwapBuffers(display, surface); // Release OpenGL resources - eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglMakeCurrent(display, + EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroySurface(display, surface); eglDestroyContext(display, context); eglTerminate(display); - dsyslog("rpihddevice: cOvg() thread ended"); + cRpiDisplay::Close(); + + DLOG("cOvg() thread ended"); } private: cCondWait* m_do; cCondWait* m_done; - cMutex* m_mutex; int m_width; int m_height; @@ -237,13 +246,13 @@ cRpiOsdProvider::cRpiOsdProvider() : cOsdProvider(), m_ovg(0) { - dsyslog("rpihddevice: new cOsdProvider()"); + DLOG("new cOsdProvider()"); m_ovg = new cOvg(); } cRpiOsdProvider::~cRpiOsdProvider() { - dsyslog("rpihddevice: delete cOsdProvider()"); + DLOG("delete cOsdProvider()"); delete m_ovg; } @@ -252,6 +261,8 @@ cOsd *cRpiOsdProvider::CreateOsd(int Left, int Top, uint Level) return new cOvgOsd(Left, Top, Level, m_ovg); } +/* ------------------------------------------------------------------------- */ + cOvgOsd::cOvgOsd(int Left, int Top, uint Level, cOvg *ovg) : cOsd(Left, Top, Level), m_ovg(ovg) @@ -304,3 +315,31 @@ void cOvgOsd::Flush(void) } } +eOsdError cOvgOsd::SetAreas(const tArea *Areas, int NumAreas) +{ + eOsdError error; + cBitmap * bitmap; + + if (Active()) + m_ovg->Clear(); + + error = cOsd::SetAreas(Areas, NumAreas); + + for (int i = 0; (bitmap = GetBitmap(i)) != NULL; i++) + bitmap->Clean(); + + return error; +} + +void cOvgOsd::SetActive(bool On) +{ + if (On != Active()) + { + cOsd::SetActive(On); + if (!On) + m_ovg->Clear(); + else + if (GetBitmap(0)) + Flush(); + } +} @@ -31,22 +31,5 @@ private: cOvg *m_ovg; }; - -class cOvgOsd : public cOsd -{ - -public: - - cOvgOsd(int Left, int Top, uint Level, cOvg *ovg); - virtual ~cOvgOsd(); - - virtual void Flush(void); - -private: - - cOvg *m_ovg; - -}; - #endif diff --git a/po/de_DE.po b/po/de_DE.po index 6976645..658e571 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: vdr-rpihddevice 0.0.4\n" "Report-Msgid-Bugs-To: <see README>\n" -"POT-Creation-Date: 2013-12-08 12:29+0100\n" +"POT-Creation-Date: 2014-01-11 15:12+0100\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" @@ -20,6 +20,9 @@ msgstr "" msgid "video format not supported!" msgstr "Videoformat nicht unterstützt!" +msgid "HD output device for Raspberry Pi" +msgstr "HD Ausgabegerät für Raspberry Pi" + msgid "analog" msgstr "analog" diff --git a/rpihddevice.c b/rpihddevice.c index 03c0813..5f22b39 100644 --- a/rpihddevice.c +++ b/rpihddevice.c @@ -10,10 +10,10 @@ #include "ovgosd.h" #include "omxdevice.h" #include "setup.h" -#include "types.h" +#include "tools.h" -static const char *VERSION = "0.0.7"; -static const char *DESCRIPTION = "HD output device for Raspberry Pi"; +static const char *VERSION = "0.0.8"; +static const char *DESCRIPTION = tr("HD output device for Raspberry Pi"); class cDummyDevice : cDevice { @@ -81,7 +81,7 @@ bool cPluginRpiHdDevice::Initialize(void) // test whether MPEG2 license is available if (!cRpiSetup::IsVideoCodecSupported(cVideoCodec::eMPEG2)) - dsyslog("rpihddevice: MPEG2 video decoder not enabled!"); + DLOG("MPEG2 video decoder not enabled!"); m_device = new cOmxDevice(&OnPrimaryDevice); @@ -98,8 +98,6 @@ bool cPluginRpiHdDevice::Start(void) void cPluginRpiHdDevice::Stop(void) { - if (m_device) - m_device->DeInit(); } cMenuSetupPage* cPluginRpiHdDevice::SetupMenu(void) @@ -27,6 +27,8 @@ void cRpiSetup::DropInstance(void) { delete s_instance; s_instance = 0; + + bcm_host_deinit(); } class cRpiSetupPage : public cMenuSetupPage @@ -77,25 +79,6 @@ private: 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")) { @@ -107,23 +90,52 @@ bool cRpiSetup::HwInit(void) } } + int height = 0, width = 0; + bool progressive = false; + cVideoPort::ePort port = cVideoPort::eComposite; + + TV_DISPLAY_STATE_T tvstate; + memset(&tvstate, 0, sizeof(TV_DISPLAY_STATE_T)); + if (!vc_tv_get_display_state(&tvstate)) + { + // HDMI + if ((tvstate.state & (VC_HDMI_HDMI | VC_HDMI_DVI))) + { + progressive = tvstate.display.hdmi.scan_mode == 0; + height = tvstate.display.hdmi.height; + width = tvstate.display.hdmi.width; + port = cVideoPort::eHDMI; + } + else + { + height = tvstate.display.sdtv.height; + width = tvstate.display.sdtv.width; + } + + ILOG("using %s video output at %dx%d%s", + cVideoPort::Str(port), width, height, progressive ? "p" : "i"); + + GetInstance()->m_isProgressive = progressive; + GetInstance()->m_displayHeight = height; + GetInstance()->m_displayWidth = width; + } + else + { + ELOG("failed to get display parameters!"); + return false; + } + return true; } bool cRpiSetup::IsAudioFormatSupported(cAudioCodec::eCodec codec, int channels, int samplingRate) { - // AAC-LATM and AAC pass-through currently not supported -// if (codec == cAudioCodec::eAAC || -// codec == cAudioCodec::eADTS) -// return false; - if (vc_tv_hdmi_audio_supported( codec == cAudioCodec::eMPG ? EDID_AudioFormat_eMPEG1 : codec == cAudioCodec::eAC3 ? EDID_AudioFormat_eAC3 : codec == cAudioCodec::eEAC3 ? EDID_AudioFormat_eEAC3 : - codec == cAudioCodec::eAAC ? EDID_AudioFormat_eAAC : - codec == cAudioCodec::eADTS ? EDID_AudioFormat_eAAC : + codec == cAudioCodec::eAAC ? EDID_AudioFormat_eAAC : EDID_AudioFormat_ePCM, channels, samplingRate == 32000 ? EDID_AudioSampleRate_e32KHz : samplingRate == 44100 ? EDID_AudioSampleRate_e44KHz : @@ -135,7 +147,7 @@ bool cRpiSetup::IsAudioFormatSupported(cAudioCodec::eCodec codec, EDID_AudioSampleSize_16bit) == 0) return true; - dsyslog("rpihddevice: %dch %s, %d.%dkHz not supported by HDMI device", + DLOG("%dch %s, %d.%dkHz not supported by HDMI device", channels, cAudioCodec::Str(codec), samplingRate / 1000, (samplingRate % 1000) / 100); @@ -144,41 +156,10 @@ bool cRpiSetup::IsAudioFormatSupported(cAudioCodec::eCodec codec, 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::IsDisplayProgressive(void) -{ - bool progressive = false; - - TV_DISPLAY_STATE_T tvstate; - memset(&tvstate, 0, sizeof(TV_DISPLAY_STATE_T)); - if (!vc_tv_get_display_state(&tvstate)) - { - // HDMI - if ((tvstate.state & (VC_HDMI_HDMI | VC_HDMI_DVI))) - progressive = tvstate.display.hdmi.scan_mode == 0; - // composite - else - progressive = false; - } - else - esyslog("rpihddevice: failed to get display state!"); - - return progressive; + height = GetInstance()->m_displayHeight; + width = GetInstance()->m_displayWidth; + aspect = (double)width / height; + return 0; } bool cRpiSetup::HasAudioSetupChanged(void) @@ -8,7 +8,7 @@ #define SETUP_H #include "omx.h" -#include "types.h" +#include "tools.h" class cRpiSetup { @@ -34,7 +34,9 @@ public: } static int GetDisplaySize(int &width, int &height, double &aspect); - static bool IsDisplayProgressive(void); + + static bool IsDisplayProgressive(void) { + return GetInstance()->m_isProgressive; } static cRpiSetup* GetInstance(void); static void DropInstance(void); @@ -44,7 +46,13 @@ public: private: - cRpiSetup() : m_audioSetupChanged(false), m_mpeg2Enabled(false) { } + cRpiSetup() : + m_audioSetupChanged(false), + m_mpeg2Enabled(false), + m_isProgressive(false), + m_displayHeight(0), + m_displayWidth(0) { } + virtual ~cRpiSetup() { } static cRpiSetup* s_instance; @@ -55,6 +63,10 @@ private: bool m_audioSetupChanged; bool m_mpeg2Enabled; + bool m_isProgressive; + + int m_displayHeight; + int m_displayWidth; }; #endif @@ -0,0 +1,90 @@ +/* + * See the README file for copyright information and how to reach the author. + * + * $Id$ + */ + +#ifndef TOOLS_H +#define TOOLS_H + +#define ELOG(a...) esyslog("rpihddevice: " a) +#define ILOG(a...) isyslog("rpihddevice: " a) +#define DLOG(a...) dsyslog("rpihddevice: " a) + +#ifdef DEBUG +#define DBG(a...) dsyslog("rpihddevice: " a) +#else +#define DBG(a...) void() +#endif + +class cAudioCodec +{ +public: + + enum eCodec { + ePCM, + eMPG, + eAC3, + eEAC3, + eAAC, + eNumCodecs, + eInvalid + }; + + static const char* Str(eCodec codec) { + return (codec == ePCM) ? "PCM" : + (codec == eMPG) ? "MPEG" : + (codec == eAC3) ? "AC3" : + (codec == eEAC3) ? "E-AC3" : + (codec == eAAC) ? "AAC" : "unknown"; + } +}; + +class cVideoCodec +{ +public: + + enum eCodec { + eMPEG2, + eH264, + eNumCodecs, + eInvalid + }; + + static const char* Str(eCodec codec) { + return (codec == eMPEG2) ? "MPEG2" : + (codec == eH264) ? "H264" : "unknown"; + } +}; + +class cAudioPort +{ +public: + + enum ePort { + eLocal, + eHDMI + }; + + static const char* Str(ePort port) { + return (port == eLocal) ? "local" : + (port == eHDMI) ? "HDMI" : "unknown"; + } +}; + +class cVideoPort +{ +public: + + enum ePort { + eComposite, + eHDMI + }; + + static const char* Str(ePort port) { + return (port == eComposite) ? "composite" : + (port == eHDMI) ? "HDMI" : "unknown"; + } +}; + +#endif |