diff --git a/projects/imx6/patches/kodi/kodi-13-implemented-rendercapture-for-latest-rendering-rework.patch b/projects/imx6/patches/kodi/kodi-13-implemented-rendercapture-for-latest-rendering-rework.patch new file mode 100644 index 0000000000..e9ae8b5ccf --- /dev/null +++ b/projects/imx6/patches/kodi/kodi-13-implemented-rendercapture-for-latest-rendering-rework.patch @@ -0,0 +1,895 @@ +From 03fe0bd48acc0399ede41e220d757ceba8be2937 Mon Sep 17 00:00:00 2001 +From: wolfgar +Date: Sat, 14 Feb 2015 03:06:03 +0100 +Subject: [PATCH] [iMX6] Implement rendercapture for latest rendering rework + blit fb using g2d when no deinterlacing is required + +--- + configure.in | 2 +- + xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp | 12 + + xbmc/cores/VideoRenderers/RenderCapture.cpp | 43 ++- + xbmc/cores/VideoRenderers/RenderCapture.h | 28 +- + .../dvdplayer/DVDCodecs/Video/DVDVideoCodecIMX.cpp | 430 +++++++++++++++------ + .../dvdplayer/DVDCodecs/Video/DVDVideoCodecIMX.h | 23 +- + 6 files changed, 401 insertions(+), 137 deletions(-) + +diff --git a/configure.in b/configure.in +index 23bf0aa..d395add 100644 +--- a/configure.in ++++ b/configure.in +@@ -1935,7 +1935,7 @@ for codecs in `echo $add_codecs | sed 's/,/ /g'`; do + ;; + *imxvpu*) + AC_CHECK_HEADER([imx-mm/vpu/vpu_wrapper.h],, AC_MSG_ERROR($missing_headers)) +- AC_CHECK_LIB([vpu], main, LIBS="$LIBS -lfslvpuwrap -lvpu", AC_MSG_ERROR($missing_library)) ++ AC_CHECK_LIB([vpu], main, LIBS="$LIBS -lfslvpuwrap -lvpu -lg2d", AC_MSG_ERROR($missing_library)) + XB_ADD_CODEC([IMXVPU], [imxvpu], [$codecs]) + CXXFLAGS="$CXXFLAGS -Wno-psabi -DLINUX " + CFLAGS="$CFLAGS -DLINUX" +diff --git a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp +index 94fb19b..d523db4 100644 +--- a/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp ++++ b/xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp +@@ -1733,6 +1733,18 @@ bool CLinuxRendererGLES::RenderCapture(CRenderCapture* capture) + return true; + } + ++#ifdef HAS_IMXVPU ++ if (m_renderMethod & RENDER_IMXMAP) ++ { ++ CRect rect(0, 0, capture->GetWidth(), capture->GetHeight()); ++ CDVDVideoCodecIMXBuffer *buffer = m_buffers[m_iYV12RenderBuffer].IMXBuffer; ++ capture->BeginRender(); ++ g_IMXContext.PushCaptureTask(buffer, &rect); ++ capture->EndRender(); ++ return true; ++ } ++#endif ++ + // save current video rect + CRect saveSize = m_destRect; + saveRotatedCoords();//backup current m_rotatedDestCoords +diff --git a/xbmc/cores/VideoRenderers/RenderCapture.cpp b/xbmc/cores/VideoRenderers/RenderCapture.cpp +index 0fac398..62605c8 100644 +--- a/xbmc/cores/VideoRenderers/RenderCapture.cpp ++++ b/xbmc/cores/VideoRenderers/RenderCapture.cpp +@@ -54,7 +54,48 @@ bool CRenderCaptureBase::UseOcclusionQuery() + return true; + } + +-#if defined(TARGET_RASPBERRY_PI) ++ ++#if defined(HAS_IMXVPU) ++CRenderCaptureIMX::CRenderCaptureIMX() ++{ ++} ++ ++CRenderCaptureIMX::~CRenderCaptureIMX() ++{ ++} ++ ++int CRenderCaptureIMX::GetCaptureFormat() ++{ ++ return CAPTUREFORMAT_RGBA; ++} ++ ++void CRenderCaptureIMX::BeginRender() ++{ ++ m_asyncChecked = true; ++ m_asyncSupported = true; ++} ++ ++void CRenderCaptureIMX::EndRender() ++{ ++ if (m_flags & CAPTUREFLAG_IMMEDIATELY) ++ ReadOut(); ++ else ++ SetState(CAPTURESTATE_NEEDSREADOUT); ++} ++ ++void* CRenderCaptureIMX::GetRenderBuffer() ++{ ++ return m_pixels; ++} ++ ++void CRenderCaptureIMX::ReadOut() ++{ ++ g_IMXContext.WaitCapture(); ++ m_pixels = reinterpret_cast(g_IMXContext.GetCaptureBuffer()); ++ SetState(CAPTURESTATE_DONE); ++} ++ ++#elif defined(TARGET_RASPBERRY_PI) + + CRenderCaptureDispmanX::CRenderCaptureDispmanX() + { +diff --git a/xbmc/cores/VideoRenderers/RenderCapture.h b/xbmc/cores/VideoRenderers/RenderCapture.h +index 56d269a..5610945 100644 +--- a/xbmc/cores/VideoRenderers/RenderCapture.h ++++ b/xbmc/cores/VideoRenderers/RenderCapture.h +@@ -172,7 +172,33 @@ class CRenderCaptureBase + bool m_asyncChecked; + }; + +-#if defined(TARGET_RASPBERRY_PI) ++ ++#if defined(HAS_IMXVPU) ++#include "../dvdplayer/DVDCodecs/Video/DVDVideoCodecIMX.h" ++ ++class CRenderCaptureIMX : public CRenderCaptureBase ++{ ++ public: ++ CRenderCaptureIMX(); ++ ~CRenderCaptureIMX(); ++ ++ int GetCaptureFormat(); ++ ++ void BeginRender(); ++ void EndRender(); ++ void ReadOut(); ++ ++ void* GetRenderBuffer(); ++}; ++ ++class CRenderCapture : public CRenderCaptureIMX ++{ ++ public: ++ CRenderCapture() {}; ++}; ++ ++ ++#elif defined(TARGET_RASPBERRY_PI) + #include "xbmc/linux/RBP.h" + + class CRenderCaptureDispmanX : public CRenderCaptureBase +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecIMX.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecIMX.cpp +index 65adced..fcd8b02 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecIMX.cpp ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecIMX.cpp +@@ -45,6 +45,8 @@ + // Global instance + CIMXContext g_IMXContext; + ++// Number of fb pages used for paning ++const int CIMXContext::m_fbPages = 2; + + // Experiments show that we need at least one more (+1) VPU buffer than the min value returned by the VPU + const int CDVDVideoCodecIMX::m_extraVpuBuffers = 1+RENDER_QUEUE_SIZE+2; +@@ -435,8 +437,7 @@ bool CDVDVideoCodecIMX::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options) + return false; + } + +- if (!g_IMXContext.Configure()) +- return false; ++ g_IMXContext.RequireConfiguration(); + + #ifdef DUMP_STREAM + m_dump = fopen("stream.dump", "wb"); +@@ -810,6 +811,7 @@ int CDVDVideoCodecIMX::Decode(BYTE *pData, int iSize, double dts, double pts) + { + goto out_error; + } ++ + } + else + { +@@ -882,7 +884,7 @@ int CDVDVideoCodecIMX::Decode(BYTE *pData, int iSize, double dts, double pts) + #endif + + #ifdef TRACE_FRAMES +- CLog::Log(LOGDEBUG, "+ %02d dts %f pts %f (VPU)\n", idx, pDvdVideoPicture->dts, pDvdVideoPicture->pts); ++ CLog::Log(LOGDEBUG, "+ %02d dts %f pts %f (VPU)\n", idx, dts, pts); + #endif + + if (!m_usePTS) +@@ -1269,12 +1271,15 @@ CDVDVideoCodecIMXBuffer::~CDVDVideoCodecIMXBuffer() + CIMXContext::CIMXContext() + : CThread("iMX IPU") + , m_fbHandle(0) +- , m_fbPages(0) ++ , m_fbCurrentPage(0) + , m_fbPhysAddr(0) + , m_fbVirtAddr(NULL) + , m_ipuHandle(0) + , m_vsync(true) + , m_pageCrops(NULL) ++ , m_g2dHandle(NULL) ++ , m_bufferCapture(NULL) ++ , m_checkConfigRequired(true) + { + // Limit queue to 2 + m_input.resize(2); +@@ -1286,8 +1291,13 @@ CIMXContext::~CIMXContext() + Close(); + } + +-bool CIMXContext::Configure(int pages) ++ ++bool CIMXContext::Configure() + { ++ ++ if (!m_checkConfigRequired) ++ return false; ++ + SetBlitRects(CRectInt(), CRectInt()); + m_fbCurrentPage = 0; + +@@ -1310,17 +1320,7 @@ bool CIMXContext::Configure(int pages) + close(fb0); + + if (m_fbHandle) +- { +- // Check for updated screen resolution +- if ((m_fbWidth != fbVar.xres) || (m_fbHeight != fbVar.yres) || (pages != m_fbPages)) +- Close(); +- else +- { +- Clear(); +- Unblank(); +- return true; +- } +- } ++ Close(); + + CLog::Log(LOGNOTICE, "iMX : Initialize render buffers\n"); + +@@ -1344,14 +1344,20 @@ bool CIMXContext::Configure(int pages) + return false; + } + +- // We want n fb pages +- m_fbPages = pages; + m_pageCrops = new CRectInt[m_fbPages]; + + m_fbVar.xoffset = 0; + m_fbVar.yoffset = 0; +- m_fbVar.bits_per_pixel = 16; +- m_fbVar.nonstd = _4CC('U', 'Y', 'V', 'Y'); ++ if (m_deInterlacing) ++ { ++ m_fbVar.nonstd = _4CC('U', 'Y', 'V', 'Y'); ++ m_fbVar.bits_per_pixel = 16; ++ } ++ else ++ { ++ m_fbVar.nonstd = _4CC('R', 'G', 'B', '4'); ++ m_fbVar.bits_per_pixel = 32; ++ } + m_fbVar.activate = FB_ACTIVATE_NOW; + m_fbVar.xres = m_fbWidth; + m_fbVar.yres = m_fbHeight; +@@ -1364,7 +1370,6 @@ bool CIMXContext::Configure(int pages) + CLog::Log(LOGWARNING, "iMX : Failed to setup %s\n", deviceName); + close(m_fbHandle); + m_fbHandle = 0; +- m_fbPages = 0; + return false; + } + +@@ -1374,7 +1379,6 @@ bool CIMXContext::Configure(int pages) + CLog::Log(LOGWARNING, "iMX : Failed to query fixed screen info at %s\n", deviceName); + close(m_fbHandle); + m_fbHandle = 0; +- m_fbPages = 0; + return false; + } + +@@ -1401,6 +1405,7 @@ bool CIMXContext::Configure(int pages) + + // Start the ipu thread + Create(); ++ m_checkConfigRequired = false; + return true; + } + +@@ -1428,7 +1433,6 @@ bool CIMXContext::Close() + { + Blank(); + close(m_fbHandle); +- m_fbPages = 0; + m_fbHandle = 0; + m_fbPhysAddr = 0; + } +@@ -1442,7 +1446,8 @@ bool CIMXContext::Close() + m_ipuHandle = 0; + } + +- CLog::Log(LOGNOTICE, "iMX : Deinitialized render context\n", m_fbPages); ++ m_checkConfigRequired = true; ++ CLog::Log(LOGNOTICE, "iMX : Deinitialized render context\n"); + + return true; + } +@@ -1503,7 +1508,14 @@ void CIMXContext::SetInterpolatedFrame(bool flag) + + void CIMXContext::SetDeInterlacing(bool flag) + { ++ bool sav_deInt = m_deInterlacing; + m_deInterlacing = flag; ++ // If deinterlacing configuration changes then fb has to be reconfigured ++ if (sav_deInt != m_deInterlacing) ++ { ++ m_checkConfigRequired = true; ++ Configure(); ++ } + } + + void CIMXContext::SetBlitRects(const CRect &srcRect, const CRect &dstRect) +@@ -1522,10 +1534,18 @@ bool CIMXContext::Blit(int page, CIMXBuffer *source_p, CIMXBuffer *source, bool + return DoTask(ipu, page); + } + +-bool CIMXContext::BlitAsync(CIMXBuffer *source_p, CIMXBuffer *source, bool topBottomFields) ++bool CIMXContext::BlitAsync(CIMXBuffer *source_p, CIMXBuffer *source, bool topBottomFields, CRect *dest) + { + IPUTask ipu; +- PrepareTask(ipu, source_p, source, topBottomFields); ++ PrepareTask(ipu, source_p, source, topBottomFields, dest); ++ return PushTask(ipu); ++} ++ ++bool CIMXContext::PushCaptureTask(CIMXBuffer *source, CRect *dest) ++{ ++ IPUTask ipu; ++ m_CaptureDone = false; ++ PrepareTask(ipu, NULL, source, false, dest); + return PushTask(ipu); + } + +@@ -1562,34 +1582,44 @@ void CIMXContext::Clear(int page) + if (!m_fbVirtAddr) return; + + uint8_t *tmp_buf; +- int pixels; ++ int bytes; + + if (page < 0) + { + tmp_buf = m_fbVirtAddr; +- pixels = m_fbPageSize*m_fbPages/2; ++ bytes = m_fbPageSize*m_fbPages; + } + else if (page < m_fbPages) + { + tmp_buf = m_fbVirtAddr + page*m_fbPageSize; +- pixels = m_fbPageSize/2; ++ bytes = m_fbPageSize; + } + else + // out of range + return; + +- for (int i = 0; i < pixels; ++i, tmp_buf += 2) ++ ++ if (m_fbVar.nonstd == _4CC('R', 'G', 'B', '4')) ++ memset(tmp_buf, 0, bytes); ++ else if (m_fbVar.nonstd == _4CC('U', 'Y', 'V', 'Y')) + { +- tmp_buf[0] = 128; +- tmp_buf[1] = 16; ++ int pixels = bytes / 2; ++ for (int i = 0; i < pixels; ++i, tmp_buf += 2) ++ { ++ tmp_buf[0] = 128; ++ tmp_buf[1] = 16; ++ } + } ++ else ++ CLog::Log(LOGERROR, "iMX Clear fb error : Unexpected format"); + } + + #define clamp_byte(x) (x<0?0:(x>255?255:x)) + + void CIMXContext::CaptureDisplay(unsigned char *buffer, int iWidth, int iHeight) + { +- if (m_fbVar.nonstd != _4CC('U', 'Y', 'V', 'Y')) ++ if ((m_fbVar.nonstd != _4CC('R', 'G', 'B', '4')) && ++ (m_fbVar.nonstd != _4CC('U', 'Y', 'V', 'Y'))) + { + CLog::Log(LOGWARNING, "iMX : Unknown screen capture format\n"); + return; +@@ -1602,56 +1632,60 @@ void CIMXContext::CaptureDisplay(unsigned char *buffer, int iWidth, int iHeight) + CLog::Log(LOGWARNING, "iMX : Invalid page to capture\n"); + return; + } +- +- int r,g,b,a; +- int u, y0, v, y1; +- + unsigned char *display = m_fbVirtAddr + m_fbCurrentPage*m_fbPageSize; +- int iStride = m_fbWidth*2; +- int oStride = iWidth*4; + +- int cy = 1*(1 << 16); +- int cr1 = 1.40200*(1 << 16); +- int cr2 = -0.71414*(1 << 16); +- int cr3 = 0*(1 << 16); +- int cb1 = 0*(1 << 16); +- int cb2 = -0.34414*(1 << 16); +- int cb3 = 1.77200*(1 << 16); ++ if (m_fbVar.nonstd != _4CC('R', 'G', 'B', '4')) ++ memcpy(buffer, display, iWidth * iHeight * 4); ++ else //_4CC('U', 'Y', 'V', 'Y'))) ++ { ++ int r,g,b,a; ++ int u, y0, v, y1; ++ int iStride = m_fbWidth*2; ++ int oStride = iWidth*4; + +- iWidth = std::min(iWidth/2, m_fbWidth/2); +- iHeight = std::min(iHeight, m_fbHeight); ++ int cy = 1*(1 << 16); ++ int cr1 = 1.40200*(1 << 16); ++ int cr2 = -0.71414*(1 << 16); ++ int cr3 = 0*(1 << 16); ++ int cb1 = 0*(1 << 16); ++ int cb2 = -0.34414*(1 << 16); ++ int cb3 = 1.77200*(1 << 16); + +- for (int y = 0; y < iHeight; ++y, display += iStride, buffer += oStride) +- { +- unsigned char *iLine = display; +- unsigned char *oLine = buffer; ++ iWidth = std::min(iWidth/2, m_fbWidth/2); ++ iHeight = std::min(iHeight, m_fbHeight); + +- for (int x = 0; x < iWidth; ++x, iLine += 4, oLine += 8 ) ++ for (int y = 0; y < iHeight; ++y, display += iStride, buffer += oStride) + { +- u = iLine[0]-128; +- y0 = iLine[1]-16; +- v = iLine[2]-128; +- y1 = iLine[3]-16; +- +- a = 255-oLine[3]; +- r = (cy*y0 + cb1*u + cr1*v) >> 16; +- g = (cy*y0 + cb2*u + cr2*v) >> 16; +- b = (cy*y0 + cb3*u + cr3*v) >> 16; +- +- oLine[0] = (clamp_byte(b)*a + oLine[0]*oLine[3])/255; +- oLine[1] = (clamp_byte(g)*a + oLine[1]*oLine[3])/255; +- oLine[2] = (clamp_byte(r)*a + oLine[2]*oLine[3])/255; +- oLine[3] = 255; +- +- a = 255-oLine[7]; +- r = (cy*y0 + cb1*u + cr1*v) >> 16; +- g = (cy*y0 + cb2*u + cr2*v) >> 16; +- b = (cy*y0 + cb3*u + cr3*v) >> 16; +- +- oLine[4] = (clamp_byte(b)*a + oLine[4]*oLine[7])/255; +- oLine[5] = (clamp_byte(g)*a + oLine[5]*oLine[7])/255; +- oLine[6] = (clamp_byte(r)*a + oLine[6]*oLine[7])/255; +- oLine[7] = 255; ++ unsigned char *iLine = display; ++ unsigned char *oLine = buffer; ++ ++ for (int x = 0; x < iWidth; ++x, iLine += 4, oLine += 8 ) ++ { ++ u = iLine[0]-128; ++ y0 = iLine[1]-16; ++ v = iLine[2]-128; ++ y1 = iLine[3]-16; ++ ++ a = 255-oLine[3]; ++ r = (cy*y0 + cb1*u + cr1*v) >> 16; ++ g = (cy*y0 + cb2*u + cr2*v) >> 16; ++ b = (cy*y0 + cb3*u + cr3*v) >> 16; ++ ++ oLine[0] = (clamp_byte(b)*a + oLine[0]*oLine[3])/255; ++ oLine[1] = (clamp_byte(g)*a + oLine[1]*oLine[3])/255; ++ oLine[2] = (clamp_byte(r)*a + oLine[2]*oLine[3])/255; ++ oLine[3] = 255; ++ ++ a = 255-oLine[7]; ++ r = (cy*y0 + cb1*u + cr1*v) >> 16; ++ g = (cy*y0 + cb2*u + cr2*v) >> 16; ++ b = (cy*y0 + cb3*u + cr3*v) >> 16; ++ ++ oLine[4] = (clamp_byte(b)*a + oLine[4]*oLine[7])/255; ++ oLine[5] = (clamp_byte(g)*a + oLine[5]*oLine[7])/255; ++ oLine[6] = (clamp_byte(r)*a + oLine[6]*oLine[7])/255; ++ oLine[7] = 255; ++ } + } + } + } +@@ -1691,16 +1725,29 @@ bool CIMXContext::PushTask(const IPUTask &task) + return true; + } + ++void CIMXContext::WaitCapture() ++{ ++ CSingleLock lk(m_monitor); ++ while (!m_CaptureDone) ++ m_inputNotFull.wait(lk); ++} ++ + void CIMXContext::PrepareTask(IPUTask &ipu, CIMXBuffer *source_p, CIMXBuffer *source, +- bool topBottomFields) ++ bool topBottomFields, CRect *dest) + { ++ Configure(); + // Fill with zeros + ipu.Zero(); + ipu.previous = source_p; + ipu.current = source; + + CRect srcRect = m_srcRect; +- CRect dstRect = m_dstRect; ++ CRect dstRect; ++ if (dest == NULL) ++ dstRect = m_dstRect; ++ else ++ dstRect = *dest; ++ + CRectInt iSrcRect, iDstRect; + + float srcWidth = srcRect.Width(); +@@ -1754,6 +1801,20 @@ void CIMXContext::PrepareTask(IPUTask &ipu, CIMXBuffer *source_p, CIMXBuffer *so + ipu.task.output.crop.w = iDstRect.Width(); + ipu.task.output.crop.h = iDstRect.Height(); + ++ // If dest is set it means we do not want to blit to frame buffer ++ // but to a capture buffer and we state this capture buffer dimensions ++ if (dest) ++ { ++ // Populate partly output block ++ ipu.task.output.crop.pos.x = 0; ++ ipu.task.output.crop.pos.y = 0; ++ ipu.task.output.crop.w = iDstRect.Width(); ++ ipu.task.output.crop.h = iDstRect.Height(); ++ ipu.task.output.width = iDstRect.Width(); ++ ipu.task.output.height = iDstRect.Height(); ++ } ++ else ++ { + // Setup deinterlacing if enabled + if (m_deInterlacing) + { +@@ -1775,6 +1836,7 @@ void CIMXContext::PrepareTask(IPUTask &ipu, CIMXBuffer *source_p, CIMXBuffer *so + else + ipu.task.input.deinterlace.field_fmt |= IPU_DEINTERLACE_FIELD_BOTTOM; + } ++ } + } + + bool CIMXContext::DoTask(IPUTask &ipu, int targetPage) +@@ -1790,16 +1852,40 @@ bool CIMXContext::DoTask(IPUTask &ipu, int targetPage) + ipu.task.input.format = ipu.current->iFormat; + ipu.task.input.paddr = ipu.current->pPhysAddr; + +- // Populate output block +- ipu.task.output.width = m_fbWidth; +- ipu.task.output.height = m_fbHeight; +- ipu.task.output.format = m_fbVar.nonstd; +- ipu.task.output.paddr = m_fbPhysAddr + targetPage*m_fbPageSize; ++ // Populate output block if it has not already been filled ++ if (ipu.task.output.width == 0) ++ { ++ ipu.task.output.width = m_fbWidth; ++ ipu.task.output.height = m_fbHeight; ++ ipu.task.output.format = m_fbVar.nonstd; ++ ipu.task.output.paddr = m_fbPhysAddr + targetPage*m_fbPageSize; + +- if (m_pageCrops[targetPage] != dstRect) ++ if (m_pageCrops[targetPage] != dstRect) ++ { ++ m_pageCrops[targetPage] = dstRect; ++ Clear(targetPage); ++ } ++ } ++ else + { +- m_pageCrops[targetPage] = dstRect; +- Clear(targetPage); ++ // If we have already set dest dimensions we want to use capture buffer ++ // Note we allocate this capture buffer as late as this function because ++ // all g2d functions have to be called from the same thread ++ int size = ipu.task.output.width * ipu.task.output.height * 4; ++ if ((m_bufferCapture) && (size != m_bufferCapture->buf_size)) ++ { ++ if (g2d_free(m_bufferCapture)) ++ CLog::Log(LOGERROR, "iMX : Error while freeing capture buuffer\n"); ++ m_bufferCapture = NULL; ++ } ++ ++ if (m_bufferCapture == NULL) ++ { ++ m_bufferCapture = g2d_alloc(size, 0); ++ if (m_bufferCapture == NULL) ++ CLog::Log(LOGERROR, "iMX : Error allocating capture buffer\n"); ++ } ++ ipu.task.output.paddr = m_bufferCapture->buf_paddr; + } + + if ((ipu.task.input.crop.w <= 0) || (ipu.task.input.crop.h <= 0) +@@ -1810,53 +1896,117 @@ bool CIMXContext::DoTask(IPUTask &ipu, int targetPage) + unsigned long long before = XbmcThreads::SystemClockMillis(); + #endif + +- int ret = IPU_CHECK_ERR_INPUT_CROP; +- while (ret != IPU_CHECK_OK && ret > IPU_CHECK_ERR_MIN) ++ if (ipu.task.input.deinterlace.enable) + { +- ret = ioctl(m_ipuHandle, IPU_CHECK_TASK, &ipu.task); +- switch (ret) ++ //We really use IPU only if we have to deinterlace (using VDIC) ++ int ret = IPU_CHECK_ERR_INPUT_CROP; ++ while (ret != IPU_CHECK_OK && ret > IPU_CHECK_ERR_MIN) + { +- case IPU_CHECK_OK: +- break; +- case IPU_CHECK_ERR_SPLIT_INPUTW_OVER: +- ipu.task.input.crop.w -= 8; +- break; +- case IPU_CHECK_ERR_SPLIT_INPUTH_OVER: +- ipu.task.input.crop.h -= 8; +- break; +- case IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER: +- ipu.task.output.crop.w -= 8; +- break; +- case IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER: +- ipu.task.output.crop.h -= 8; +- break; +- default: +- CLog::Log(LOGWARNING, "iMX : unhandled IPU check error: %d\n", ret); ++ ret = ioctl(m_ipuHandle, IPU_CHECK_TASK, &ipu.task); ++ switch (ret) ++ { ++ case IPU_CHECK_OK: ++ break; ++ case IPU_CHECK_ERR_SPLIT_INPUTW_OVER: ++ ipu.task.input.crop.w -= 8; ++ break; ++ case IPU_CHECK_ERR_SPLIT_INPUTH_OVER: ++ ipu.task.input.crop.h -= 8; ++ break; ++ case IPU_CHECK_ERR_SPLIT_OUTPUTW_OVER: ++ ipu.task.output.crop.w -= 8; ++ break; ++ case IPU_CHECK_ERR_SPLIT_OUTPUTH_OVER: ++ ipu.task.output.crop.h -= 8; ++ break; ++ default: ++ CLog::Log(LOGWARNING, "iMX : unhandled IPU check error: %d\n", ret); ++ return false; ++ } ++ } ++ ++ // Need to find another interface to protect ipu.current from disposing ++ // in CDVDVideoCodecIMX::Dispose. CIMXContext must not have knowledge ++ // about CDVDVideoCodecIMX. ++ ipu.current->BeginRender(); ++ if (ipu.current->IsValid()) ++ ret = ioctl(m_ipuHandle, IPU_QUEUE_TASK, &ipu.task); ++ else ++ ret = 0; ++ ipu.current->EndRender(); ++ ++ if (ret < 0) ++ { ++ CLog::Log(LOGERROR, "IPU task failed: %s\n", strerror(errno)); + return false; + } +- } + +- // Need to find another interface to protect ipu.current from disposing +- // in CDVDVideoCodecIMX::Dispose. CIMXContext must not have knowledge +- // about CDVDVideoCodecIMX. +- ipu.current->BeginRender(); +- if (ipu.current->IsValid()) +- ret = ioctl(m_ipuHandle, IPU_QUEUE_TASK, &ipu.task); ++ // Duplicate 2nd scandline if double rate is active ++ if (ipu.task.input.deinterlace.field_fmt & IPU_DEINTERLACE_RATE_EN) ++ { ++ uint8_t *pageAddr = m_fbVirtAddr + targetPage*m_fbPageSize; ++ memcpy(pageAddr, pageAddr+m_fbLineLength, m_fbLineLength); ++ } ++ } + else +- ret = 0; +- ipu.current->EndRender(); +- +- if (ret < 0) + { +- CLog::Log(LOGERROR, "IPU task failed: %s\n", strerror(errno)); +- return false; +- } ++ // deinterlacing is not required, let's use g2d instead of IPU + +- // Duplicate 2nd scandline if double rate is active +- if (ipu.task.input.deinterlace.field_fmt & IPU_DEINTERLACE_RATE_EN) +- { +- uint8_t *pageAddr = m_fbVirtAddr + targetPage*m_fbPageSize; +- memcpy(pageAddr, pageAddr+m_fbLineLength, m_fbLineLength); ++ struct g2d_surface src, dst; ++ memset(&src, 0, sizeof(src)); ++ memset(&dst, 0, sizeof(dst)); ++ ++ ipu.current->BeginRender(); ++ if (ipu.current->IsValid()) ++ { ++ if (ipu.current->iFormat == _4CC('I', '4', '2', '0')) ++ { ++ src.format = G2D_I420; ++ src.planes[0] = ipu.current->pPhysAddr; ++ src.planes[1] = src.planes[0] + Align(ipu.current->iWidth * ipu.current->iHeight, 64); ++ src.planes[2] = src.planes[1] + Align((ipu.current->iWidth * ipu.current->iHeight) / 2, 64); ++ } ++ else //_4CC('N', 'V', '1', '2'); ++ { ++ src.format = G2D_NV12; ++ src.planes[0] = ipu.current->pPhysAddr; ++ src.planes[1] = src.planes[0] + Align(ipu.current->iWidth * ipu.current->iHeight, 64); ++ } ++ ++ src.left = ipu.task.input.crop.pos.x; ++ src.right = ipu.task.input.crop.w + src.left ; ++ src.top = ipu.task.input.crop.pos.y; ++ src.bottom = ipu.task.input.crop.h + src.top; ++ src.stride = ipu.current->iWidth; ++ src.width = ipu.current->iWidth; ++ src.height = ipu.current->iHeight; ++ src.rot = G2D_ROTATION_0; ++ /*printf("src planes :%x -%x -%x \n",src.planes[0], src.planes[1], src.planes[2] ); ++ printf("src left %d right %d top %d bottom %d stride %d w : %d h %d rot : %d\n", ++ src.left, src.right, src.top, src.bottom, src.stride, src.width, src.height, src.rot);*/ ++ ++ dst.planes[0] = ipu.task.output.paddr; ++ dst.left = ipu.task.output.crop.pos.x; ++ dst.top = ipu.task.output.crop.pos.y; ++ dst.right = ipu.task.output.crop.w + dst.left; ++ dst.bottom = ipu.task.output.crop.h + dst.top; ++ ++ dst.stride = ipu.task.output.width; ++ dst.width = ipu.task.output.width; ++ dst.height = ipu.task.output.height; ++ dst.rot = G2D_ROTATION_0; ++ dst.format = G2D_RGBA8888; ++ /*printf("dst planes :%x -%x -%x \n",dst.planes[0], dst.planes[1], dst.planes[2] ); ++ printf("dst left %d right %d top %d bottom %d stride %d w : %d h %d rot : %d format %d\n", ++ dst.left, dst.right, dst.top, dst.bottom, dst.stride, dst.width, dst.height, dst.rot, dst.format);*/ ++ ++ // Launch synchronous blit ++ g2d_blit(m_g2dHandle, &src, &dst); ++ g2d_finish(m_g2dHandle); ++ if ((m_bufferCapture) && (ipu.task.output.paddr == m_bufferCapture->buf_paddr)) ++ m_CaptureDone = true; ++ } ++ ipu.current->EndRender(); + } + + #ifdef IMX_PROFILE_BUFFERS +@@ -1891,6 +2041,16 @@ void CIMXContext::Process() + bool ret, useBackBuffer; + int backBuffer; + ++ // open g2d here to ensure all g2d fucntions are called from the same thread ++ if (m_g2dHandle == NULL) ++ { ++ if (g2d_open(&m_g2dHandle) != 0) ++ { ++ m_g2dHandle = NULL; ++ CLog::Log(LOGERROR, "%s - Error while trying open G2D\n", __FUNCTION__); ++ } ++ } ++ + while (!m_bStop) + { + { +@@ -1903,6 +2063,10 @@ void CIMXContext::Process() + useBackBuffer = m_vsync; + IPUTask &task = m_input[m_beginInput]; + backBuffer = useBackBuffer?1-m_fbCurrentPage:m_fbCurrentPage; ++ ++ // Hack to detect we deal with capture buffer ++ if (task.task.output.width != 0) ++ useBackBuffer = false; + ret = DoTask(task, backBuffer); + + // Free resources +@@ -1926,5 +2090,19 @@ void CIMXContext::Process() + --m_bufferedInput; + } + ++ // close g2d here to ensure all g2d fucntions are called from the same thread ++ if (m_bufferCapture) ++ { ++ if (g2d_free(m_bufferCapture)) ++ CLog::Log(LOGERROR, "iMX : Failed to free capture buffers\n"); ++ m_bufferCapture = NULL; ++ } ++ if (m_g2dHandle) ++ { ++ if (g2d_close(m_g2dHandle)) ++ CLog::Log(LOGERROR, "iMX : Error while closing G2D\n"); ++ m_g2dHandle = NULL; ++ } ++ + return; + } +diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecIMX.h b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecIMX.h +index dca3f2f..e414454 100644 +--- a/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecIMX.h ++++ b/xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecIMX.h +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + + + // The decoding format of the VPU buffer. Comment this to decode +@@ -93,17 +94,15 @@ public: + CIMXContext(); + ~CIMXContext(); + +- bool Configure(int pages = 2); ++ void RequireConfiguration() { m_checkConfigRequired = true; } ++ bool Configure(); + bool Close(); + + bool Blank(); + bool Unblank(); + bool SetVSync(bool enable); + +- bool IsValid() const { return m_fbPages > 0; } +- +- // Returns the number of available pages +- int PageCount() const { return m_fbPages; } ++ bool IsValid() const { return m_checkConfigRequired == false; } + + // Populates a CIMXBuffer with attributes of a page + bool GetPageInfo(CIMXBuffer *info, int page); +@@ -127,7 +126,7 @@ public: + // been queued. BlitAsync renders always to the current backbuffer and + // swaps the pages. + bool BlitAsync(CIMXBuffer *source_p, CIMXBuffer *source, +- bool topBottomFields = true); ++ bool topBottomFields = true, CRect *dest = NULL); + + // Shows a page vsynced + bool ShowPage(int page); +@@ -141,6 +140,9 @@ public: + // Captures the current visible frame buffer page and blends it into + // the passed overlay. The buffer format is BGRA (4 byte) + void CaptureDisplay(unsigned char *buffer, int iWidth, int iHeight); ++ bool PushCaptureTask(CIMXBuffer *source, CRect *dest); ++ void *GetCaptureBuffer() const { if (m_bufferCapture) return m_bufferCapture->buf_vaddr; else return NULL; } ++ void WaitCapture(); + + private: + struct IPUTask +@@ -167,7 +169,7 @@ private: + + bool PushTask(const IPUTask &); + void PrepareTask(IPUTask &ipu, CIMXBuffer *source_p, CIMXBuffer *source, +- bool topBottomFields); ++ bool topBottomFields, CRect *dest = NULL); + bool DoTask(IPUTask &ipu, int targetPage); + + virtual void OnStartup(); +@@ -179,7 +181,6 @@ private: + typedef std::vector TaskQueue; + + int m_fbHandle; +- int m_fbPages; + int m_fbCurrentPage; + int m_fbWidth; + int m_fbHeight; +@@ -206,6 +207,12 @@ private: + XbmcThreads::ConditionVariable m_inputNotEmpty; + XbmcThreads::ConditionVariable m_inputNotFull; + mutable CCriticalSection m_monitor; ++ ++ void *m_g2dHandle; ++ struct g2d_buf *m_bufferCapture; ++ bool m_CaptureDone; ++ bool m_checkConfigRequired; ++ static const int m_fbPages; + }; + + +-- +1.9.1 +