diff options
author | Martijn Kaijser <machine.sanctum@gmail.com> | 2014-09-23 08:42:14 +0200 |
---|---|---|
committer | Martijn Kaijser <machine.sanctum@gmail.com> | 2014-09-23 08:42:14 +0200 |
commit | 079b6cd2bbec860a651de82137eb52009ceb66e3 (patch) | |
tree | 58a6077d2754a9479de793b2be903b77852284a5 | |
parent | c945c4a72f658a97176fe68d7531e9aeab948dde (diff) | |
parent | 77778e4e1ef549455574f765ff6252fbb278effa (diff) |
Merge pull request #5348 from Phaeodaria/tegra-omx-pr
[OMX] Revive OMX backend for Tegra
-rw-r--r-- | configure.in | 11 | ||||
-rw-r--r-- | xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp | 88 | ||||
-rw-r--r-- | xbmc/cores/VideoRenderers/LinuxRendererGLES.h | 6 | ||||
-rw-r--r-- | xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp | 2 | ||||
-rw-r--r-- | xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h | 4 | ||||
-rw-r--r-- | xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp | 12 | ||||
-rw-r--r-- | xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h | 1 | ||||
-rw-r--r-- | xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.cpp | 10 | ||||
-rw-r--r-- | xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.h | 17 | ||||
-rw-r--r-- | xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp | 516 | ||||
-rw-r--r-- | xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h | 72 |
11 files changed, 541 insertions, 198 deletions
diff --git a/configure.in b/configure.in index 490b12be36..50eb8b4722 100644 --- a/configure.in +++ b/configure.in @@ -190,7 +190,7 @@ vaapi_disabled="== VAAPI support manually disabled. ==" vtbdecoder_enabled="== VTBDecoder support enabled. ==" vtbdecoder_disabled="== VTBDecoder support manually disabled. ==" openmax_disabled="== OpenMax support manually disabled. ==" -openmax_not_found="== Could not find libnvomx. OpenMax support disabled. ==" +openmax_not_found="== Could not find OpenMax headers. OpenMax support disabled. ==" ssh_not_found="== Could not find libssh. ==" ssh_disabled="== SSH SFTP disabled. ==" librtmp_not_found="== Could not find libRTMP. RTMP support disabled. ==" @@ -1881,12 +1881,13 @@ elif test "$target_platform" = "target_raspberry_pi"; then AC_MSG_NOTICE($openmax_disabled) else if test "$use_gles" = "yes" && test "$use_openmax" = "auto"; then - PKG_CHECK_MODULES([OPENMAX], [libomxil-bellagio], - USE_OPENMAX=1;[INCLUDES="$INCLUDES $OPENMAX_CFLAGS"; LIBS="$LIBS $OPENMAX_LIBS"], + + AC_CHECK_HEADERS([OMX_Core.h], + USE_OPENMAX=1;[INCLUDES="$INCLUDES $OPENMAX_CFLAGS"; LIBS="$LIBS $OPENMAX_LIBS";AC_DEFINE([HAVE_LIBOPENMAX], [1], [Define to 1 if you have the OpenMax library.])], use_openmax=no;USE_OPENMAX=0;AC_MSG_RESULT($openmax_not_found)) elif test "$use_gles" = "yes" && test "$use_openmax" = "yes"; then - PKG_CHECK_MODULES([OPENMAX], [libomxil-bellagio], - USE_OPENMAX=1;[INCLUDES="$INCLUDES $OPENMAX_CFLAGS"; LIBS="$LIBS $OPENMAX_LIBS"], + AC_CHECK_HEADERS([OMX_Core.h], + USE_OPENMAX=1;[INCLUDES="$INCLUDES $OPENMAX_CFLAGS"; LIBS="$LIBS $OPENMAX_LIBS";AC_DEFINE([HAVE_LIBOPENMAX], [1], [Define to 1 if you have the OpenMax library.])], AC_MSG_ERROR($openmax_not_found)) else AC_MSG_NOTICE($openmax_disabled) diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp index 88292d4a1d..1300a7f71a 100644 --- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp +++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp @@ -79,6 +79,12 @@ static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR; static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; #endif +#if defined(EGL_KHR_reusable_sync) +static PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR; +static PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR; +static PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR; +#endif + #ifdef HAS_IMXVPU #include "windowing/egl/EGLWrapper.h" #include "DVDCodecs/Video/DVDVideoCodecIMX.h" @@ -102,7 +108,7 @@ CLinuxRendererGLES::YUVBUFFER::YUVBUFFER() memset(&image , 0, sizeof(image)); flipindex = 0; #ifdef HAVE_LIBOPENMAX - openMaxBuffer = NULL; + openMaxBufferHolder = NULL; #endif #ifdef HAVE_VIDEOTOOLBOXDECODER cvBufferRef = NULL; @@ -167,6 +173,19 @@ CLinuxRendererGLES::CLinuxRendererGLES() if (!glEGLImageTargetTexture2DOES) glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) CEGLWrapper::GetProcAddress("glEGLImageTargetTexture2DOES"); #endif + +#if defined(EGL_KHR_reusable_sync) + if (!eglCreateSyncKHR) { + eglCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC) eglGetProcAddress("eglCreateSyncKHR"); + } + if (!eglDestroySyncKHR) { + eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC) eglGetProcAddress("eglDestroySyncKHR"); + } + if (!eglClientWaitSyncKHR) { + eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC) eglGetProcAddress("eglClientWaitSyncKHR"); + } +#endif + #ifdef HAS_IMXVPU if (!glTexDirectVIVMap) glTexDirectVIVMap = (PFNGLTEXDIRECTVIVMAPPROC) CEGLWrapper::GetProcAddress("glTexDirectVIVMap"); @@ -842,7 +861,13 @@ void CLinuxRendererGLES::LoadShaders(int field) m_textureCreate = &CLinuxRendererGLES::CreateIMXMAPTexture; m_textureDelete = &CLinuxRendererGLES::DeleteIMXMAPTexture; } - else + else if (m_format == RENDER_FMT_OMXEGL) + { + m_textureUpload = &CLinuxRendererGLES::UploadOpenMaxTexture; + m_textureCreate = &CLinuxRendererGLES::CreateOpenMaxTexture; + m_textureDelete = &CLinuxRendererGLES::DeleteOpenMaxTexture; + } + else { // default to YV12 texture handlers m_textureUpload = &CLinuxRendererGLES::UploadYV12Texture; @@ -1372,7 +1397,12 @@ void CLinuxRendererGLES::RenderSoftware(int index, int field) void CLinuxRendererGLES::RenderOpenMax(int index, int field) { #if defined(HAVE_LIBOPENMAX) - GLuint textureId = m_buffers[index].openMaxBuffer->texture_id; + OpenMaxVideoBufferHolder *bufferHolder = m_buffers[index].openMaxBufferHolder; + if (!bufferHolder) + return; + OpenMaxVideoBuffer *buffer = bufferHolder->m_openMaxVideoBuffer; + + GLuint textureId = buffer->texture_id; glDisable(GL_DEPTH_TEST); @@ -1411,9 +1441,9 @@ void CLinuxRendererGLES::RenderOpenMax(int index, int field) // Set texture coordinates tex[0][0] = tex[3][0] = 0; - tex[0][1] = tex[1][1] = 0; + tex[0][1] = tex[1][1] = 1; tex[1][0] = tex[2][0] = 1; - tex[2][1] = tex[3][1] = 1; + tex[2][1] = tex[3][1] = 0; glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, idx); @@ -1427,6 +1457,15 @@ void CLinuxRendererGLES::RenderOpenMax(int index, int field) glDisable(m_textureTarget); VerifyGLState(); + +#if defined(EGL_KHR_reusable_sync) + if (buffer->eglSync) { + eglDestroySyncKHR(buffer->eglDisplay, buffer->eglSync); + } + buffer->eglSync = eglCreateSyncKHR(buffer->eglDisplay, EGL_SYNC_FENCE_KHR, NULL); + glFlush(); // flush to ensure the sync later will succeed +#endif + #endif } @@ -1787,7 +1826,7 @@ void CLinuxRendererGLES::UploadYV12Texture(int source) #if defined(HAVE_LIBOPENMAX) - if (!(im->flags&IMAGE_FLAG_READY) || m_buffers[source].openMaxBuffer) + if (!(im->flags&IMAGE_FLAG_READY) || m_buffers[source].openMaxBufferHolder) #else if (!(im->flags&IMAGE_FLAG_READY)) #endif @@ -1820,9 +1859,9 @@ void CLinuxRendererGLES::UploadYV12Texture(int source) SWS_FAST_BILINEAR, NULL, NULL, NULL); uint8_t *src[] = { im->plane[0], im->plane[1], im->plane[2], 0 }; - int srcStride[] = { im->stride[0], im->stride[1], im->stride[2], 0 }; + int srcStride[] = { int(im->stride[0]), int(im->stride[1]), int(im->stride[2]), 0 }; uint8_t *dst[] = { m_rgbBuffer, 0, 0, 0 }; - int dstStride[] = { m_sourceWidth*4, 0, 0, 0 }; + int dstStride[] = { int(m_sourceWidth*4), 0, 0, 0 }; sws_scale(m_sw_context, src, srcStride, 0, im->height, dst, dstStride); } } @@ -2613,6 +2652,31 @@ bool CLinuxRendererGLES::CreateSurfaceTexture(int index) return true; } +//******************************************************************************************************** +// SurfaceTexture creation, deletion, copying + clearing +//******************************************************************************************************** +void CLinuxRendererGLES::UploadOpenMaxTexture(int index) +{ +} + +void CLinuxRendererGLES::DeleteOpenMaxTexture(int index) +{ +#ifdef HAVE_LIBOPENMAX + if (m_buffers[index].openMaxBufferHolder) { + m_buffers[index].openMaxBufferHolder->Release(); + m_buffers[index].openMaxBufferHolder = 0; + } +#endif +} + +bool CLinuxRendererGLES::CreateOpenMaxTexture(int index) +{ +#ifdef HAVE_LIBOPENMAX m_buffers[index].openMaxBufferHolder = 0; +#endif + return true; +} + + void CLinuxRendererGLES::SetTextureFilter(GLenum method) { for (int i = 0 ; i<m_NumYV12Buffers ; i++) @@ -2902,7 +2966,13 @@ unsigned int CLinuxRendererGLES::GetOptimalBufferSize() void CLinuxRendererGLES::AddProcessor(COpenMax* openMax, DVDVideoPicture *picture, int index) { YUVBUFFER &buf = m_buffers[index]; - buf.openMaxBuffer = picture->openMaxBuffer; + if (buf.openMaxBufferHolder) { + buf.openMaxBufferHolder->Release(); + } + buf.openMaxBufferHolder = picture->openMaxBufferHolder; + if (buf.openMaxBufferHolder) { + buf.openMaxBufferHolder->Acquire(); + } } #endif #ifdef HAVE_VIDEOTOOLBOXDECODER diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGLES.h b/xbmc/cores/VideoRenderers/LinuxRendererGLES.h index 4be992d413..d8bf35dfda 100644 --- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.h +++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.h @@ -213,6 +213,10 @@ protected: void DeleteSurfaceTexture(int index); bool CreateSurfaceTexture(int index); + void UploadOpenMaxTexture(int index); + void DeleteOpenMaxTexture(int index); + bool CreateOpenMaxTexture(int index); + void UploadIMXMAPTexture(int index); void DeleteIMXMAPTexture(int index); bool CreateIMXMAPTexture(int index); @@ -281,7 +285,7 @@ protected: unsigned flipindex; /* used to decide if this has been uploaded */ #ifdef HAVE_LIBOPENMAX - OpenMaxVideoBuffer *openMaxBuffer; + OpenMaxVideoBufferHolder *openMaxBufferHolder; #endif #ifdef HAVE_VIDEOTOOLBOXDECODER struct __CVBuffer *cvBufferRef; diff --git a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp index f8a5635cf5..01ed3433bd 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp +++ b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp @@ -159,7 +159,7 @@ CDVDVideoCodec* CDVDFactoryCodec::CreateVideoCodec(CDVDStreamInfo &hint, unsigne #else hwSupport += "MediaCodec:no "; #endif -#if defined(HAVE_LIBOPENMAX) && defined(TARGET_POSIX) +#if defined(HAVE_LIBOPENMAX) hwSupport += "OpenMax:yes "; #elif defined(TARGET_POSIX) hwSupport += "OpenMax:no "; diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h index 8333fb4177..6f3c51b2d8 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h @@ -52,7 +52,7 @@ namespace VAAPI { class CVaapiRenderPicture; } namespace VDPAU { class CVdpauRenderPicture; } class COpenMax; class COpenMaxVideo; -struct OpenMaxVideoBuffer; +struct OpenMaxVideoBufferHolder; class CDVDVideoCodecStageFright; class CDVDMediaCodecInfo; class CDVDVideoCodecIMXBuffer; @@ -83,7 +83,7 @@ struct DVDVideoPicture struct { COpenMax *openMax; - OpenMaxVideoBuffer *openMaxBuffer; + OpenMaxVideoBufferHolder *openMaxBufferHolder; }; struct { diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp index b2e781660b..73ddbe196d 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.cpp @@ -30,6 +30,7 @@ #include "DVDVideoCodecOpenMax.h" #include "OpenMaxVideo.h" #include "utils/log.h" +#include "settings/Settings.h" #define CLASSNAME "COpenMax" //////////////////////////////////////////////////////////////////////////////////////////// @@ -121,7 +122,7 @@ void CDVDVideoCodecOpenMax::Dispose() if (m_omx_decoder) { m_omx_decoder->Close(); - delete m_omx_decoder; + m_omx_decoder->Release(); m_omx_decoder = NULL; } if (m_videobuffer.iFlags & DVP_FLAG_ALLOCATED) @@ -175,7 +176,7 @@ int CDVDVideoCodecOpenMax::Decode(uint8_t* pData, int iSize, double dts, double return rtn; } - return VC_BUFFER; + return m_omx_decoder->Decode(0, 0, 0, 0); } void CDVDVideoCodecOpenMax::Reset(void) @@ -188,9 +189,16 @@ bool CDVDVideoCodecOpenMax::GetPicture(DVDVideoPicture* pDvdVideoPicture) m_omx_decoder->GetPicture(&m_videobuffer); *pDvdVideoPicture = m_videobuffer; + // TODO what's going on here? bool is required as return value. return VC_PICTURE | VC_BUFFER; } +bool CDVDVideoCodecOpenMax::ClearPicture(DVDVideoPicture* pDvdVideoPicture) +{ + return m_omx_decoder->ClearPicture(pDvdVideoPicture); +} + + //////////////////////////////////////////////////////////////////////////////////////////// bool CDVDVideoCodecOpenMax::bitstream_convert_init(void *in_extradata, int in_extrasize) { diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h index fb80d02959..34a1b222bb 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecOpenMax.h @@ -36,6 +36,7 @@ public: virtual int Decode(uint8_t *pData, int iSize, double dts, double pts); virtual void Reset(void); virtual bool GetPicture(DVDVideoPicture *pDvdVideoPicture); + virtual bool ClearPicture(DVDVideoPicture* pDvdVideoPicture); virtual void SetDropState(bool bDrop); virtual const char* GetName(void) { return (const char*)m_pFormatName; } diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.cpp index 7339728065..d026ac4c36 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.cpp +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.cpp @@ -24,8 +24,8 @@ #include "system.h" #endif -#if defined(HAVE_LIBOPENMAX) #include "OpenMax.h" +#if defined(HAVE_LIBOPENMAX) #include "DynamicDll.h" #include "DVDClock.h" #include "DVDStreamInfo.h" @@ -43,7 +43,6 @@ #define CLASSNAME "COpenMax" - //////////////////////////////////////////////////////////////////////////////////////////// class DllLibOpenMaxInterface { @@ -62,7 +61,7 @@ public: class DllLibOpenMax : public DllDynamic, DllLibOpenMaxInterface { - DECLARE_DLL_WRAPPER(DllLibOpenMax, "/usr/lib/libnvomx.so") + DECLARE_DLL_WRAPPER(DllLibOpenMax, "libnvomx.so") DEFINE_METHOD0(OMX_ERRORTYPE, OMX_Init) DEFINE_METHOD0(OMX_ERRORTYPE, OMX_Deinit) @@ -105,7 +104,7 @@ COpenMax::COpenMax() m_omx_decoder = NULL; m_omx_client_state = DEAD; m_omx_decoder_state = 0; - sem_init(m_omx_decoder_state_change, 0, 0); + sem_init(&m_omx_decoder_state_change, 0, 0); /* m_omx_flush_input = (sem_t*)malloc(sizeof(sem_t)); sem_init(m_omx_flush_input, 0, 0); @@ -119,6 +118,7 @@ COpenMax::~COpenMax() #if defined(OMX_DEBUG_VERBOSE) CLog::Log(LOGDEBUG, "%s::%s\n", CLASSNAME, __func__); #endif + sem_destroy(&m_omx_decoder_state_change); /* sem_destroy(m_omx_flush_input); free(m_omx_flush_input); @@ -182,7 +182,7 @@ OMX_ERRORTYPE COpenMax::WaitForState(OMX_STATETYPE state) { clock_gettime(CLOCK_REALTIME, &timeout); timeout.tv_sec += 1; - sem_timedwait(m_omx_decoder_state_change, &timeout); + sem_timedwait(&m_omx_decoder_state_change, &timeout); if (errno == ETIMEDOUT) tries++; if (tries > 5) diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.h index a14b8e43b4..b9eb7e2806 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.h +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMax.h @@ -29,6 +29,21 @@ #include <semaphore.h> #include <OMX_Core.h> + +// check for potentially undefined OpenMAX version numbers +#ifndef OMX_VERSION_MAJOR +#define OMX_VERSION_MAJOR 1 +#endif +#ifndef OMX_VERSION_MINOR +#define OMX_VERSION_MINOR 1 +#endif +#ifndef OMX_VERSION_REVISION +#define OMX_VERSION_REVISION 2 +#endif +#ifndef OMX_VERSION_STEP +#define OMX_VERSION_STEP 0 +#endif + //////////////////////////////////////////////////////////////////////////////////////////// // debug spew defines #if 0 @@ -105,7 +120,7 @@ protected: // OpenMax state tracking OMX_CLIENT_STATE m_omx_client_state; volatile int m_omx_decoder_state; - sem_t *m_omx_decoder_state_change; + sem_t m_omx_decoder_state_change; std::vector<omx_codec_capability> m_omx_decoder_capabilities; private: diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp index dcbdb1e7b7..d5b562b576 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.cpp @@ -32,6 +32,7 @@ #include "DVDVideoCodec.h" #include "utils/log.h" #include "utils/TimeUtils.h" +#include "guilib/GUIWindowManager.h" #include "ApplicationMessenger.h" #include "Application.h" @@ -40,6 +41,12 @@ #include <OMX_Index.h> #include <OMX_Image.h> +#if 1 +//#define OMX_DEBUG_EMPTYBUFFERDONE +#define OMX_DEBUG_VERBOSE +//#define OMX_DEBUG_EMPTYBUFFERDONE +//#define OMX_DEBUG_FILLBUFFERDONE +#endif #define CLASSNAME "COpenMaxVideo" @@ -58,6 +65,13 @@ // EGL extension functions static PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR; static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR; + +#if defined(EGL_KHR_reusable_sync) +static PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR; +static PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR; +static PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR; +#endif + #define GETEXTENSION(type, ext) \ do \ { \ @@ -76,16 +90,14 @@ do \ (a).nVersion.s.nRevision = OMX_VERSION_REVISION; \ (a).nVersion.s.nStep = OMX_VERSION_STEP +pthread_mutex_t m_omx_queue_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; COpenMaxVideo::COpenMaxVideo() { m_portChanging = false; - pthread_mutex_init(&m_omx_input_mutex, NULL); - pthread_mutex_init(&m_omx_output_mutex, NULL); + pthread_cond_init(&m_omx_queue_available, NULL); - m_omx_decoder_state_change = (sem_t*)malloc(sizeof(sem_t)); - sem_init(m_omx_decoder_state_change, 0, 0); memset(&m_videobuffer, 0, sizeof(DVDVideoPicture)); m_drop_state = false; m_decoded_width = 0; @@ -104,10 +116,6 @@ COpenMaxVideo::~COpenMaxVideo() #endif if (m_is_open) Close(); - pthread_mutex_destroy(&m_omx_input_mutex); - pthread_mutex_destroy(&m_omx_output_mutex); - sem_destroy(m_omx_decoder_state_change); - free(m_omx_decoder_state_change); } bool COpenMaxVideo::Open(CDVDStreamInfo &hints) @@ -258,9 +266,9 @@ void COpenMaxVideo::SetDropState(bool bDrop) { OMX_ERRORTYPE omx_err; - // blow all but the last ready video frame - pthread_mutex_lock(&m_omx_output_mutex); - while (m_omx_output_ready.size() > 1) + // blow all video frames + pthread_mutex_lock(&m_omx_queue_mutex); + while (m_omx_output_ready.size() > 0) { m_dts_queue.pop(); OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_output_ready.front()->omx_buffer; @@ -271,7 +279,11 @@ void COpenMaxVideo::SetDropState(bool bDrop) CLog::Log(LOGERROR, "%s::%s - OMX_FillThisBuffer, omx_err(0x%x)\n", CLASSNAME, __func__, omx_err); } - pthread_mutex_unlock(&m_omx_output_mutex); + + // And ingore all queued elements + ReleaseDemuxQueue(); + + pthread_mutex_unlock(&m_omx_queue_mutex); #if defined(OMX_DEBUG_VERBOSE) CLog::Log(LOGDEBUG, "%s::%s - m_drop_state(%d)\n", @@ -280,8 +292,53 @@ void COpenMaxVideo::SetDropState(bool bDrop) } } +int COpenMaxVideo::EnqueueDemuxPacket(omx_demux_packet demux_packet) +{ + if (m_omx_decoder_state != OMX_StateExecuting) { + return 0; + } + OMX_ERRORTYPE omx_err; + OMX_BUFFERHEADERTYPE* omx_buffer; + + // need to lock here to retreve an input buffer and pop the queue + omx_buffer = m_omx_input_avaliable.front(); + m_omx_input_avaliable.pop(); + + // delete the previous demuxer buffer + delete [] omx_buffer->pBuffer; + // setup a new omx_buffer. + omx_buffer->nFlags = m_omx_input_eos ? OMX_BUFFERFLAG_EOS : 0; + omx_buffer->nOffset = 0; + omx_buffer->pBuffer = demux_packet.buff; + omx_buffer->nAllocLen = demux_packet.size; + omx_buffer->nFilledLen = demux_packet.size; + omx_buffer->nTimeStamp = (demux_packet.pts == DVD_NOPTS_VALUE) ? 0 : demux_packet.pts * 1000.0; // in microseconds; + omx_buffer->pAppPrivate = omx_buffer; + omx_buffer->nInputPortIndex = m_omx_input_port; + +#if defined(OMX_DEBUG_EMPTYBUFFERDONE) + CLog::Log(LOGDEBUG, + "%s::%s - feeding decoder, omx_buffer->pBuffer(0x%p), demuxer_bytes(%d)\n", + CLASSNAME, __func__, omx_buffer->pBuffer, demux_packet.size); +#endif + // Give this omx_buffer to OpenMax to be decoded. + omx_err = OMX_EmptyThisBuffer(m_omx_decoder, omx_buffer); + if (omx_err) { + CLog::Log(LOGDEBUG, + "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", + CLASSNAME, __func__, omx_err); + return VC_ERROR; + } + // only push if we are successful with feeding OMX_EmptyThisBuffer + m_dts_queue.push(demux_packet.dts); + + return 0; +} + int COpenMaxVideo::Decode(uint8_t* pData, int iSize, double dts, double pts) { + pthread_mutex_lock(&m_omx_queue_mutex); + if (pData) { int demuxer_bytes = iSize; @@ -295,61 +352,18 @@ int COpenMaxVideo::Decode(uint8_t* pData, int iSize, double dts, double pts) demux_packet.pts = pts; demux_packet.size = demuxer_bytes; + // TODO memory leak? where does this memory get does get freed? demux_packet.buff = new OMX_U8[demuxer_bytes]; memcpy(demux_packet.buff, demuxer_content, demuxer_bytes); + m_demux_queue.push(demux_packet); - // we can look at m_omx_input_avaliable.empty without needing to lock/unlock - // try to send any/all demux packets to omx decoder. - while (!m_omx_input_avaliable.empty() && !m_demux_queue.empty() ) + while(!m_omx_input_avaliable.empty() && !m_demux_queue.empty()) { - OMX_ERRORTYPE omx_err; - OMX_BUFFERHEADERTYPE* omx_buffer; - demux_packet = m_demux_queue.front(); m_demux_queue.pop(); - // need to lock here to retreve an input buffer and pop the queue - pthread_mutex_lock(&m_omx_input_mutex); - omx_buffer = m_omx_input_avaliable.front(); - m_omx_input_avaliable.pop(); - pthread_mutex_unlock(&m_omx_input_mutex); - - // delete the previous demuxer buffer - delete [] omx_buffer->pBuffer; - // setup a new omx_buffer. - omx_buffer->nFlags = m_omx_input_eos ? OMX_BUFFERFLAG_EOS : 0; - omx_buffer->nOffset = 0; - omx_buffer->pBuffer = demux_packet.buff; - omx_buffer->nAllocLen = demux_packet.size; - omx_buffer->nFilledLen = demux_packet.size; - omx_buffer->nTimeStamp = (demux_packet.pts == DVD_NOPTS_VALUE) ? 0 : demux_packet.pts * 1000.0; // in microseconds; - omx_buffer->pAppPrivate = omx_buffer; - omx_buffer->nInputPortIndex = m_omx_input_port; - - #if defined(OMX_DEBUG_EMPTYBUFFERDONE) - CLog::Log(LOGDEBUG, - "%s::%s - feeding decoder, omx_buffer->pBuffer(0x%p), demuxer_bytes(%d)\n", - CLASSNAME, __func__, omx_buffer->pBuffer, demuxer_bytes); - #endif - // Give this omx_buffer to OpenMax to be decoded. - omx_err = OMX_EmptyThisBuffer(m_omx_decoder, omx_buffer); - if (omx_err) - { - CLog::Log(LOGDEBUG, - "%s::%s - OMX_EmptyThisBuffer() failed with result(0x%x)\n", - CLASSNAME, __func__, omx_err); - return VC_ERROR; - } - // only push if we are successful with feeding OMX_EmptyThisBuffer - m_dts_queue.push(demux_packet.dts); - - // if m_omx_input_avaliable and/or demux_queue are now empty, - // wait up to 20ms for OpenMax to consume a demux packet - if (m_omx_input_avaliable.empty() || m_demux_queue.empty()) - m_input_consumed_event.WaitMSec(1); + EnqueueDemuxPacket(demux_packet); } - if (m_omx_input_avaliable.empty() && !m_demux_queue.empty()) - m_input_consumed_event.WaitMSec(1); #if defined(OMX_DEBUG_VERBOSE) if (m_omx_input_avaliable.empty()) @@ -359,10 +373,31 @@ int COpenMaxVideo::Decode(uint8_t* pData, int iSize, double dts, double pts) #endif } - if (m_omx_output_ready.empty()) - return VC_BUFFER; + int returnCode = VC_BUFFER; + + if (m_omx_input_avaliable.empty() && m_omx_output_ready.empty()) { + // Sleep for some time until either an image has been decoded or there's space in the input buffer again + struct timespec timeout; + clock_gettime(CLOCK_REALTIME, &timeout); + timeout.tv_nsec += 100000000; // 100ms, 1ms still shows the stuttering + if (timeout.tv_nsec >= 1000000000) { + timeout.tv_sec += 1; + timeout.tv_nsec -= 1000000000; + } + pthread_cond_timedwait(&m_omx_queue_available, &m_omx_queue_mutex, &timeout); + } - return VC_PICTURE | VC_BUFFER; + if (!m_omx_output_ready.empty()) { + returnCode |= VC_PICTURE; + } + if (!m_omx_input_avaliable.empty()) { + returnCode |= VC_BUFFER; + } + + pthread_mutex_unlock(&m_omx_queue_mutex); + + + return returnCode; } void COpenMaxVideo::Reset(void) @@ -370,81 +405,116 @@ void COpenMaxVideo::Reset(void) #if defined(OMX_DEBUG_VERBOSE) CLog::Log(LOGDEBUG, "%s::%s\n", CLASSNAME, __func__); #endif -/* + // only reset OpenMax decoder if it's running if (m_omx_decoder_state == OMX_StateExecuting) { - OMX_ERRORTYPE omx_err; - - omx_err = StopDecoder(); + StopDecoder(); // Alloc OpenMax input buffers. - omx_err = AllocOMXInputBuffers(); + AllocOMXInputBuffers(); // Alloc OpenMax output buffers. - omx_err = AllocOMXOutputBuffers(); + AllocOMXOutputBuffers(); - omx_err = StartDecoder(); + StartDecoder(); + + // TODO error checking? } -*/ ::Sleep(100); } -bool COpenMaxVideo::GetPicture(DVDVideoPicture* pDvdVideoPicture) +bool COpenMaxVideo::ClearPicture(DVDVideoPicture* pDvdVideoPicture) { - while (m_omx_output_busy.size() > 1) + if (pDvdVideoPicture->openMaxBufferHolder) { + pDvdVideoPicture->openMaxBufferHolder->Release(); + pDvdVideoPicture->openMaxBufferHolder = 0; + } + return true; +} + +void COpenMaxVideo::ReleaseBuffer(OpenMaxVideoBuffer* releaseBuffer) +{ + if (!releaseBuffer) + return; + + // TODO this is NOT multithreading safe. Buffer lifetime managment needs to be adopted. + + pthread_mutex_lock(&m_omx_queue_mutex); + OpenMaxVideoBuffer *buffer = releaseBuffer; + +#if defined(EGL_KHR_reusable_sync) + if (buffer->eglSync) { + eglClientWaitSyncKHR(m_egl_display, buffer->eglSync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, 1*1000*1000*1000); + eglDestroySyncKHR(m_egl_display, buffer->eglSync); + buffer->eglSync = 0; + } +#endif + + bool done = !!(buffer->omx_buffer->nFlags & OMX_BUFFERFLAG_EOS) | buffer->done; + if (!done) { - // fetch a output buffer and pop it off the busy list - pthread_mutex_lock(&m_omx_output_mutex); - OpenMaxVideoBuffer *buffer = m_omx_output_busy.front(); - m_omx_output_busy.pop(); - pthread_mutex_unlock(&m_omx_output_mutex); - - bool done = buffer->omx_buffer->nFlags & OMX_BUFFERFLAG_EOS; - if (!done) - { - // return the omx buffer back to OpenMax to fill. - OMX_ERRORTYPE omx_err = OMX_FillThisBuffer(m_omx_decoder, buffer->omx_buffer); - if (omx_err) - CLog::Log(LOGERROR, "%s::%s - OMX_FillThisBuffer, omx_err(0x%x)\n", - CLASSNAME, __func__, omx_err); - } + // return the omx buffer back to OpenMax to fill. + + OMX_ERRORTYPE omx_err = OMX_FillThisBuffer(m_omx_decoder, buffer->omx_buffer); + if (omx_err) + CLog::Log(LOGERROR, "%s::%s - OMX_FillThisBuffer, omx_err(0x%x)\n", CLASSNAME, __func__, omx_err); } + pthread_mutex_unlock(&m_omx_queue_mutex); +} + +int COpenMaxVideo::GetPicture(DVDVideoPicture* pDvdVideoPicture) +{ + int returnCode = 0; + pDvdVideoPicture->openMaxBufferHolder = 0; if (!m_omx_output_ready.empty()) { OpenMaxVideoBuffer *buffer; // fetch a output buffer and pop it off the ready list - pthread_mutex_lock(&m_omx_output_mutex); + pthread_mutex_lock(&m_omx_queue_mutex); buffer = m_omx_output_ready.front(); m_omx_output_ready.pop(); - m_omx_output_busy.push(buffer); - pthread_mutex_unlock(&m_omx_output_mutex); + pthread_mutex_unlock(&m_omx_queue_mutex); - pDvdVideoPicture->dts = DVD_NOPTS_VALUE; - pDvdVideoPicture->pts = DVD_NOPTS_VALUE; - pDvdVideoPicture->format = RENDER_FMT_OMXEGL; - pDvdVideoPicture->openMax = this; - pDvdVideoPicture->openMaxBuffer = buffer; + if(m_drop_state) { + // return the omx buffer back to OpenMax to fill. - if (!m_dts_queue.empty()) - { - pDvdVideoPicture->dts = m_dts_queue.front(); - m_dts_queue.pop(); + OMX_ERRORTYPE omx_err = OMX_FillThisBuffer(m_omx_decoder, buffer->omx_buffer); + if (omx_err) + CLog::Log(LOGERROR, "%s::%s - OMX_FillThisBuffer, omx_err(0x%x)\n", + CLASSNAME, __func__, omx_err); + } + else { + pDvdVideoPicture->dts = DVD_NOPTS_VALUE; + pDvdVideoPicture->pts = DVD_NOPTS_VALUE; + pDvdVideoPicture->format = RENDER_FMT_OMXEGL; + pDvdVideoPicture->openMax = this; + pDvdVideoPicture->openMaxBufferHolder = new OpenMaxVideoBufferHolder(buffer); + + if (!m_dts_queue.empty()) + { + pDvdVideoPicture->dts = m_dts_queue.front(); + m_dts_queue.pop(); + } + // nTimeStamp is in microseconds + pDvdVideoPicture->pts = (buffer->omx_buffer->nTimeStamp == 0) ? DVD_NOPTS_VALUE : (double)buffer->omx_buffer->nTimeStamp / 1000.0; + + returnCode |= VC_PICTURE; } - // nTimeStamp is in microseconds - pDvdVideoPicture->pts = (buffer->omx_buffer->nTimeStamp == 0) ? DVD_NOPTS_VALUE : (double)buffer->omx_buffer->nTimeStamp / 1000.0; } - #if defined(OMX_DEBUG_VERBOSE) else { + #if defined(OMX_DEBUG_VERBOSE) CLog::Log(LOGDEBUG, "%s::%s - called but m_omx_output_ready is empty\n", CLASSNAME, __func__); - } #endif + } pDvdVideoPicture->iFlags = DVP_FLAG_ALLOCATED; pDvdVideoPicture->iFlags |= m_drop_state ? DVP_FLAG_DROPPED : 0; - return true; + returnCode |= m_omx_input_avaliable.empty() ? 0 : VC_BUFFER; + + return returnCode; } @@ -455,17 +525,27 @@ OMX_ERRORTYPE COpenMaxVideo::DecoderEmptyBufferDone( OMX_BUFFERHEADERTYPE* pBuffer) { COpenMaxVideo *ctx = static_cast<COpenMaxVideo*>(pAppData); -/* + #if defined(OMX_DEBUG_EMPTYBUFFERDONE) CLog::Log(LOGDEBUG, "%s::%s - buffer_size(%lu), timestamp(%f)\n", CLASSNAME, __func__, pBuffer->nFilledLen, (double)pBuffer->nTimeStamp / 1000.0); #endif -*/ + // queue free input buffer to avaliable list. - pthread_mutex_lock(&ctx->m_omx_input_mutex); + pthread_mutex_lock(&m_omx_queue_mutex); ctx->m_omx_input_avaliable.push(pBuffer); - ctx->m_input_consumed_event.Set(); - pthread_mutex_unlock(&ctx->m_omx_input_mutex); + if(!ctx->m_omx_input_avaliable.empty()) { + if (!ctx->m_demux_queue.empty()) { + omx_demux_packet demux_packet = m_demux_queue.front(); + ctx->m_demux_queue.pop(); + ctx->EnqueueDemuxPacket(demux_packet); + } + else { + pthread_cond_signal(&m_omx_queue_available); + } + } + + pthread_mutex_unlock(&m_omx_queue_mutex); return OMX_ErrorNone; } @@ -487,9 +567,10 @@ OMX_ERRORTYPE COpenMaxVideo::DecoderFillBufferDone( if (!ctx->m_portChanging) { // queue output omx buffer to ready list. - pthread_mutex_lock(&ctx->m_omx_output_mutex); + pthread_mutex_lock(&m_omx_queue_mutex); ctx->m_omx_output_ready.push(buffer); - pthread_mutex_unlock(&ctx->m_omx_output_mutex); + pthread_cond_signal(&m_omx_queue_available); + pthread_mutex_unlock(&m_omx_queue_mutex); } return OMX_ErrorNone; @@ -556,8 +637,8 @@ OMX_ERRORTYPE COpenMaxVideo::AllocOMXInputBuffers(void) #if defined(OMX_DEBUG_VERBOSE) CLog::Log(LOGDEBUG, - "%s::%s - iport(%d), nBufferCountMin(%lu), nBufferSize(%lu)\n", - CLASSNAME, __func__, m_omx_input_port, port_format.nBufferCountMin, port_format.nBufferSize); + "%s::%s - iport(%d), nBufferCountMin(%lu), nBufferCountActual(%lu), nBufferSize(%lu)\n", + CLASSNAME, __func__, m_omx_input_port, port_format.nBufferCountMin, port_format.nBufferCountActual, port_format.nBufferSize); #endif for (size_t i = 0; i < port_format.nBufferCountMin; i++) { @@ -585,14 +666,12 @@ OMX_ERRORTYPE COpenMaxVideo::FreeOMXInputBuffers(bool wait) { OMX_ERRORTYPE omx_err = OMX_ErrorNone; - /* omx_err = OMX_SendCommand(m_omx_decoder, OMX_CommandFlush, m_omx_input_port, 0); if (omx_err) CLog::Log(LOGERROR, "%s::%s - OMX_CommandFlush failed with omx_err(0x%x)\n", CLASSNAME, __func__, omx_err); - else if (wait) - sem_wait(m_omx_flush_input); - */ + //else if (wait) + // sem_wait(m_omx_flush_input); // free omx input port buffers. for (size_t i = 0; i < m_omx_input_buffers.size(); i++) @@ -622,10 +701,40 @@ void COpenMaxVideo::CallbackAllocOMXEGLTextures(void *userdata) omx->AllocOMXOutputEGLTextures(); } -void COpenMaxVideo::CallbackFreeOMXEGLTextures(void *userdata) +struct DeleteImageInfo { + ThreadMessageCallback callback; + EGLImageKHR egl_image; +#if defined(EGL_KHR_reusable_sync) + EGLSyncKHR egl_sync; +#endif + GLuint texture_id; +}; + +void OpenMaxDeleteTextures(void *userdata) { - COpenMaxVideo *omx = static_cast<COpenMaxVideo*>(userdata); - omx->FreeOMXOutputEGLTextures(true); + EGLDisplay eglDisplay = eglGetCurrentDisplay(); + EGLContext eglContext = eglGetCurrentContext(); + + if (!eglDestroyImageKHR) + { + GETEXTENSION(PFNEGLDESTROYIMAGEKHRPROC, eglDestroyImageKHR); + } + + DeleteImageInfo *deleteInfo = (DeleteImageInfo*)userdata; + +#if defined(EGL_KHR_reusable_sync) + if (deleteInfo->egl_sync) { + eglClientWaitSyncKHR(eglDisplay, deleteInfo->egl_sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, 1*1000*1000*1000); + eglDestroySyncKHR(eglDisplay, deleteInfo->egl_sync); + } +#endif + + // destroy egl_image + eglDestroyImageKHR(eglDisplay, deleteInfo->egl_image); + // free texture + glDeleteTextures(1, &deleteInfo->texture_id); + + delete deleteInfo; } OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputBuffers(void) @@ -646,7 +755,7 @@ OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputBuffers(void) tMsg.dwMessage = TMSG_CALLBACK; tMsg.lpVoid = (void*)&callbackData; - g_application.getApplicationMessenger().SendMessage(tMsg, true); + CApplicationMessenger::Get().SendMessage(tMsg, true); omx_err = OMX_ErrorNone; } @@ -656,7 +765,18 @@ OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputBuffers(void) OMX_ERRORTYPE COpenMaxVideo::FreeOMXOutputBuffers(bool wait) { - OMX_ERRORTYPE omx_err = FreeOMXOutputEGLTextures(wait); + OMX_ERRORTYPE omx_err; + + for (size_t i = 0; i < m_omx_output_buffers.size(); i++) + { + OpenMaxVideoBuffer* egl_buffer = m_omx_output_buffers[i]; + egl_buffer->done = true; + + // tell decoder output port to stop using the EGLImage + omx_err = OMX_FreeBuffer(m_omx_decoder, m_omx_output_port, egl_buffer->omx_buffer); + egl_buffer->Release(); + } + m_omx_output_buffers.clear(); return omx_err; } @@ -668,6 +788,11 @@ OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputEGLTextures(void) if (!eglCreateImageKHR) { GETEXTENSION(PFNEGLCREATEIMAGEKHRPROC, eglCreateImageKHR); +#if defined(EGL_KHR_reusable_sync) + GETEXTENSION(PFNEGLCREATESYNCKHRPROC, eglCreateSyncKHR); + GETEXTENSION(PFNEGLDESTROYSYNCKHRPROC, eglDestroySyncKHR); + GETEXTENSION(PFNEGLCLIENTWAITSYNCKHRPROC, eglClientWaitSyncKHR); +#endif } EGLint attrib = EGL_NONE; @@ -679,22 +804,24 @@ OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputEGLTextures(void) port_format.nPortIndex = m_omx_output_port; omx_err = OMX_GetParameter(m_omx_decoder, OMX_IndexParamPortDefinition, &port_format); - #if defined(OMX_DEBUG_VERBOSE) +// #if defined(OMX_DEBUG_VERBOSE) CLog::Log(LOGDEBUG, - "%s::%s (1) - oport(%d), nFrameWidth(%lu), nFrameHeight(%lu), nStride(%lx), nBufferCountMin(%lu), nBufferSize(%lu)\n", + "%s::%s (1) - oport(%d), nFrameWidth(%lu), nFrameHeight(%lu), nStride(%lx), nBufferCountMin(%lu), nBufferCountActual(%lu), nBufferSize(%lu)\n", CLASSNAME, __func__, m_omx_output_port, port_format.format.video.nFrameWidth, port_format.format.video.nFrameHeight,port_format.format.video.nStride, - port_format.nBufferCountMin, port_format.nBufferSize); - #endif + port_format.nBufferCountMin, port_format.nBufferCountActual, port_format.nBufferSize); +// #endif glActiveTexture(GL_TEXTURE0); - for (size_t i = 0; i < port_format.nBufferCountMin; i++) + for (size_t i = 0; i < 4 * port_format.nBufferCountMin; i++) { egl_buffer = new OpenMaxVideoBuffer; - memset(egl_buffer, 0, sizeof(*egl_buffer)); + egl_buffer->SetOpenMaxVideo(this); egl_buffer->width = m_decoded_width; egl_buffer->height = m_decoded_height; + egl_buffer->done = false; + egl_buffer->eglDisplay = m_egl_display; glGenTextures(1, &egl_buffer->texture_id); glBindTexture(GL_TEXTURE_2D, egl_buffer->texture_id); @@ -710,8 +837,10 @@ OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputEGLTextures(void) GL_RGBA, // format GL_UNSIGNED_BYTE, // type NULL); // pixels -- will be provided later - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // create EGLImage from texture egl_buffer->egl_image = eglCreateImageKHR( @@ -732,51 +861,23 @@ OMX_ERRORTYPE COpenMaxVideo::AllocOMXOutputEGLTextures(void) m_omx_decoder, &egl_buffer->omx_buffer, m_omx_output_port, egl_buffer, egl_buffer->egl_image); if (omx_err) { - CLog::Log(LOGERROR, "%s::%s - OMX_UseEGLImage failed with omx_err(0x%x)\n", - CLASSNAME, __func__, omx_err); + CLog::Log(LOGERROR, "%s::%s - OMX_UseEGLImage %d failed with omx_err(0x%x)\n", + CLASSNAME, __func__, i, omx_err); return(omx_err); } + m_omx_output_buffers.push_back(egl_buffer); CLog::Log(LOGDEBUG, "%s::%s - Texture %p Width %d Height %d\n", CLASSNAME, __func__, egl_buffer->egl_image, egl_buffer->width, egl_buffer->height); } m_omx_output_eos = false; - while (!m_omx_output_busy.empty()) - m_omx_output_busy.pop(); while (!m_omx_output_ready.empty()) m_omx_output_ready.pop(); return omx_err; } -OMX_ERRORTYPE COpenMaxVideo::FreeOMXOutputEGLTextures(bool wait) -{ - OMX_ERRORTYPE omx_err = OMX_ErrorNone; - OpenMaxVideoBuffer *egl_buffer; - - if (!eglDestroyImageKHR) - { - GETEXTENSION(PFNEGLDESTROYIMAGEKHRPROC, eglDestroyImageKHR); - } - - for (size_t i = 0; i < m_omx_output_buffers.size(); i++) - { - egl_buffer = m_omx_output_buffers[i]; - // tell decoder output port to stop using the EGLImage - omx_err = OMX_FreeBuffer(m_omx_decoder, m_omx_output_port, egl_buffer->omx_buffer); - // destroy egl_image - eglDestroyImageKHR(m_egl_display, egl_buffer->egl_image); - // free texture - glDeleteTextures(1, &egl_buffer->texture_id); - delete egl_buffer; - } - m_omx_output_buffers.clear(); - - return omx_err; -} - - //////////////////////////////////////////////////////////////////////////////////////////// // DecoderEventHandler -- OMX event callback OMX_ERRORTYPE COpenMaxVideo::DecoderEventHandler( @@ -831,7 +932,7 @@ OMX_ERRORTYPE COpenMaxVideo::DecoderEventHandler( CLASSNAME, __func__, ctx->m_omx_decoder_state); break; } - sem_post(ctx->m_omx_decoder_state_change); + sem_post(&ctx->m_omx_decoder_state_change); break; case OMX_CommandFlush: /* @@ -970,7 +1071,7 @@ OMX_ERRORTYPE COpenMaxVideo::DecoderEventHandler( sem_post(ctx->m_omx_flush_input); sem_post(ctx->m_omx_flush_output); */ - sem_post(ctx->m_omx_decoder_state_change); + sem_post(&ctx->m_omx_decoder_state_change); break; default: CLog::Log(LOGWARNING, @@ -1029,7 +1130,7 @@ OMX_ERRORTYPE COpenMaxVideo::StopDecoder(void) { CLog::Log(LOGERROR, "%s::%s - setting OMX_StateIdle failed with omx_err(0x%x)\n", CLASSNAME, __func__, omx_err); - return omx_err; + //return omx_err; } // we can free our allocated port buffers in OMX_StateIdle state. @@ -1038,6 +1139,9 @@ OMX_ERRORTYPE COpenMaxVideo::StopDecoder(void) // free OpenMax output buffers. FreeOMXOutputBuffers(true); + // free all queues demux packets + ReleaseDemuxQueue(); + // transition decoder component from idle to loaded omx_err = SetStateForComponent(OMX_StateLoaded); if (omx_err) @@ -1048,3 +1152,87 @@ OMX_ERRORTYPE COpenMaxVideo::StopDecoder(void) return omx_err; } +void COpenMaxVideo::ReleaseDemuxQueue(void) +{ + while(!m_demux_queue.empty()) { + delete[] m_demux_queue.front().buff; + m_demux_queue.pop(); + } +} + +OpenMaxVideoBufferHolder::OpenMaxVideoBufferHolder(OpenMaxVideoBuffer *openMaxVideoBuffer) +{ + m_openMaxVideoBuffer = openMaxVideoBuffer; + if (m_openMaxVideoBuffer) { + m_openMaxVideoBuffer->Acquire(); + } +} + +OpenMaxVideoBufferHolder::~OpenMaxVideoBufferHolder() +{ + if (m_openMaxVideoBuffer) { + m_openMaxVideoBuffer->PassBackToRenderer(); + m_openMaxVideoBuffer->Release(); + } +} + +OpenMaxVideoBuffer::OpenMaxVideoBuffer() +{ + m_openMaxVideo = 0; +} + +OpenMaxVideoBuffer::~OpenMaxVideoBuffer() +{ + ReleaseTexture(); + if (m_openMaxVideo) { + m_openMaxVideo->Release(); + } +} + +void OpenMaxVideoBuffer::SetOpenMaxVideo(COpenMaxVideo *openMaxVideo) +{ + if (m_openMaxVideo) { + m_openMaxVideo->Release(); + } + + m_openMaxVideo = openMaxVideo; + + if (m_openMaxVideo) { + m_openMaxVideo->Acquire(); + } +} + +void OpenMaxVideoBuffer::PassBackToRenderer() +{ + m_openMaxVideo->ReleaseBuffer(this); +} + +void OpenMaxVideoBuffer::ReleaseTexture() +{ + DeleteImageInfo *deleteInfo = new DeleteImageInfo; + + // add egl resources to deletion info + // TODO delete from constructor! + deleteInfo->egl_image = egl_image; + deleteInfo->egl_sync = eglSync; + deleteInfo->texture_id = texture_id; + + if ( g_application.IsCurrentThread() ) + { + OpenMaxDeleteTextures(deleteInfo); + } + else + { + // TODO put the callbackData pointer into userptr so that it can be delete afterwards + deleteInfo->callback.callback = &OpenMaxDeleteTextures; + deleteInfo->callback.userptr = (void *)deleteInfo; + + ThreadMessage tMsg; + tMsg.dwMessage = TMSG_CALLBACK; + tMsg.lpVoid = (void*)&deleteInfo->callback; + + // HACK, this should be synchronous, but it's not possible since Stop blocks the GUI thread. + CApplicationMessenger::Get().SendMessage(tMsg, false); + } + +} diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h index e06c41de07..a235a015b4 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h +++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/OpenMaxVideo.h @@ -24,9 +24,38 @@ #include "OpenMax.h" #include <EGL/egl.h> #include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <DVDResource.h> + +template<typename T> struct IDVDResourceCounted2 +{ + IDVDResourceCounted2() : m_refs(1) {} + virtual ~IDVDResourceCounted2() {} + virtual T* Acquire() + { + printf("Acquire %p %d\n", this, m_refs); + ++m_refs; + return (T*)this; + } + + virtual long Release() + { + printf("Release %p %d\n", this, m_refs); + --m_refs; + assert(m_refs >= 0); + if (m_refs == 0) delete (T*)this; + return m_refs; + } + int m_refs; +}; + // an omx egl video frame -typedef struct OpenMaxVideoBuffer { +struct OpenMaxVideoBuffer : public IDVDResourceCounted<OpenMaxVideoBuffer> { + OpenMaxVideoBuffer(); + virtual ~OpenMaxVideoBuffer(); + OMX_BUFFERHEADERTYPE *omx_buffer; int width; int height; @@ -35,9 +64,30 @@ typedef struct OpenMaxVideoBuffer { // used for egl based rendering if active EGLImageKHR egl_image; GLuint texture_id; -} OpenMaxVideoBuffer; -class COpenMaxVideo : public COpenMax +#if defined(EGL_KHR_reusable_sync) + EGLSyncKHR eglSync; +#endif + EGLDisplay eglDisplay; + + bool done; + void PassBackToRenderer(); + void ReleaseTexture(); + void SetOpenMaxVideo(COpenMaxVideo *openMaxVideo); + +private: + COpenMaxVideo *m_openMaxVideo; +}; + +class OpenMaxVideoBufferHolder : public IDVDResourceCounted<OpenMaxVideoBufferHolder> { +public: + OpenMaxVideoBufferHolder(OpenMaxVideoBuffer *openMaxVideoBuffer); + virtual ~OpenMaxVideoBufferHolder(); + + OpenMaxVideoBuffer *m_openMaxVideoBuffer; +}; + +class COpenMaxVideo : public COpenMax, public IDVDResourceCounted<COpenMaxVideo> { public: COpenMaxVideo(); @@ -48,9 +98,13 @@ public: void Close(void); int Decode(uint8_t *pData, int iSize, double dts, double pts); void Reset(void); - bool GetPicture(DVDVideoPicture *pDvdVideoPicture); + int GetPicture(DVDVideoPicture *pDvdVideoPicture); + bool ClearPicture(DVDVideoPicture *pDvdVideoPicture); + void ReleaseBuffer(OpenMaxVideoBuffer *buffer); void SetDropState(bool bDrop); protected: + int EnqueueDemuxPacket(omx_demux_packet demux_packet); + void QueryCodec(void); OMX_ERRORTYPE PrimeFillBuffers(void); OMX_ERRORTYPE AllocOMXInputBuffers(void); @@ -59,13 +113,13 @@ protected: OMX_ERRORTYPE FreeOMXOutputBuffers(bool wait); static void CallbackAllocOMXEGLTextures(void*); OMX_ERRORTYPE AllocOMXOutputEGLTextures(void); - static void CallbackFreeOMXEGLTextures(void*); - OMX_ERRORTYPE FreeOMXOutputEGLTextures(bool wait); // TODO Those should move into the base class. After start actions can be executed by callbacks. OMX_ERRORTYPE StartDecoder(void); OMX_ERRORTYPE StopDecoder(void); + void ReleaseDemuxQueue(); + // OpenMax decoder callback routines. virtual OMX_ERRORTYPE DecoderEventHandler(OMX_HANDLETYPE hComponent, OMX_PTR pAppData, OMX_EVENTTYPE eEvent, OMX_U32 nData1, OMX_U32 nData2, OMX_PTR pEventData); @@ -87,8 +141,11 @@ protected: std::queue<double> m_dts_queue; std::queue<omx_demux_packet> m_demux_queue; + // Synchronization + //pthread_mutex_t m_omx_queue_mutex; + pthread_cond_t m_omx_queue_available; + // OpenMax input buffers (demuxer packets) - pthread_mutex_t m_omx_input_mutex; std::queue<OMX_BUFFERHEADERTYPE*> m_omx_input_avaliable; std::vector<OMX_BUFFERHEADERTYPE*> m_omx_input_buffers; bool m_omx_input_eos; @@ -97,7 +154,6 @@ protected: CEvent m_input_consumed_event; // OpenMax output buffers (video frames) - pthread_mutex_t m_omx_output_mutex; std::queue<OpenMaxVideoBuffer*> m_omx_output_busy; std::queue<OpenMaxVideoBuffer*> m_omx_output_ready; std::vector<OpenMaxVideoBuffer*> m_omx_output_buffers; |