diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/AMLCodec.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/AMLCodec.cpp index f714721a8cf39..73507c099e800 100644 --- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/AMLCodec.cpp +++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/AMLCodec.cpp @@ -54,12 +54,49 @@ extern "C" { #include #include #include +#include // amcodec include extern "C" { #include } // extern "C" +class PosixFile +{ +public: + PosixFile() : + m_fd(-1) + { + } + + PosixFile(int fd) : + m_fd(fd) + { + } + + ~PosixFile() + { + if (m_fd >= 0) + close(m_fd); + } + + bool Open(const std::string &pathName, int flags) + { + m_fd = open(pathName.c_str(), flags); + return m_fd >= 0; + } + + int GetDescriptor() const { return m_fd; } + + int IOControl(unsigned long request, void *param) + { + return ioctl(m_fd, request, param); + } + +private: + int m_fd; +}; + typedef struct { bool noblock; int video_pid; @@ -364,27 +401,6 @@ void dumpfile_write(am_private_t *para, void* buf, int bufsiz) write(para->dumpfile, buf, bufsiz); } -/*************************************************************************/ -/*************************************************************************/ -static int64_t get_pts_video() -{ - int fd = open("/sys/class/tsync/pts_video", O_RDONLY); - if (fd >= 0) - { - char pts_str[16]; - int size = read(fd, pts_str, sizeof(pts_str)); - close(fd); - if (size > 0) - { - unsigned long pts = strtoul(pts_str, NULL, 16); - return pts; - } - } - - CLog::Log(LOGERROR, "get_pts_video: open /tsync/event error"); - return -1; -} - static int set_pts_pcrscr(int64_t value) { int fd = open("/sys/class/tsync/pts_pcrscr", O_WRONLY); @@ -1418,9 +1434,6 @@ bool CAMLCodec::OpenDecoder(CDVDStreamInfo &hints) m_speed = DVD_PLAYSPEED_NORMAL; m_1st_pts = 0; m_cur_pts = 0; - m_player_pts = 0; - m_cur_pictcnt = 0; - m_old_pictcnt = 0; m_dst_rect.SetRect(0, 0, 0, 0); m_zoom = -1; m_contrast = -1; @@ -1430,6 +1443,12 @@ bool CAMLCodec::OpenDecoder(CDVDStreamInfo &hints) m_start_pts = 0; m_hints = hints; + if (!OpenAmlVideo(hints)) + { + CLog::Log(LOGERROR, "CAMLCodec::OpenDecoder - cannot open amlvideo device"); + return false; + } + ShowMainVideo(false); am_packet_init(&am_private->am_pkt); @@ -1652,6 +1671,53 @@ bool CAMLCodec::OpenDecoder(CDVDStreamInfo &hints) return true; } +bool CAMLCodec::OpenAmlVideo(const CDVDStreamInfo &hints) +{ + PosixFilePtr amlVideoFile = std::make_shared(); + if (!amlVideoFile->Open("/dev/video10", O_RDONLY | O_NONBLOCK)) + { + CLog::Log(LOGERROR, "CAMLCodec::OpenAmlVideo - cannot open V4L amlvideo device /dev/video10: %s", strerror(errno)); + return false; + } + + m_amlVideoFile = amlVideoFile; + + m_defaultVfmMap = GetVfmMap("default"); + SetVfmMap("default", "decoder ppmgr deinterlace amlvideo amvideo"); + + SysfsUtils::SetInt("/sys/module/amlvideodri/parameters/freerun_mode", 1); + + return true; +} + +std::string CAMLCodec::GetVfmMap(const std::string &name) +{ + std::string vfmMap; + SysfsUtils::GetString("/sys/class/vfm/map", vfmMap); + std::vector sections = StringUtils::Split(vfmMap, '\n'); + std::string sectionMap; + for (size_t i = 0; i < sections.size(); ++i) + { + if (StringUtils::StartsWith(sections[i], name + " {")) + { + sectionMap = sections[i]; + break; + } + } + + int openingBracePos = sectionMap.find('{') + 1; + sectionMap = sectionMap.substr(openingBracePos, sectionMap.size() - openingBracePos - 1); + StringUtils::Replace(sectionMap, "(0)", ""); + + return sectionMap; +} + +void CAMLCodec::SetVfmMap(const std::string &name, const std::string &map) +{ + SysfsUtils::SetString("/sys/class/vfm/map", "rm " + name); + SysfsUtils::SetString("/sys/class/vfm/map", "add " + name + " " + map); +} + void CAMLCodec::CloseDecoder() { CLog::Log(LOGDEBUG, "CAMLCodec::CloseDecoder"); @@ -1674,6 +1740,14 @@ void CAMLCodec::CloseDecoder() SysfsUtils::SetInt("/sys/class/tsync/enable", 1); ShowMainVideo(false); + + CloseAmlVideo(); +} + +void CAMLCodec::CloseAmlVideo() +{ + m_amlVideoFile.reset(); + SetVfmMap("default", m_defaultVfmMap); } void CAMLCodec::Reset() @@ -1711,8 +1785,7 @@ void CAMLCodec::Reset() // reset some interal vars m_1st_pts = 0; m_cur_pts = 0; - m_cur_pictcnt = 0; - m_old_pictcnt = 0; + m_ptsQueue.clear(); SetSpeed(m_speed); } @@ -1723,7 +1796,6 @@ int CAMLCodec::Decode(uint8_t *pData, size_t iSize, double dts, double pts) if (pData) { - m_player_pts = pts; am_private->am_pkt.data = pData; am_private->am_pkt.data_size = iSize; @@ -1744,7 +1816,6 @@ int CAMLCodec::Decode(uint8_t *pData, size_t iSize, double dts, double pts) if (am_private->am_pkt.avpts != (int64_t)AV_NOPTS_VALUE) am_private->am_pkt.avpts -= m_start_pts; - // handle dts, including 31bit wrap, aml can only handle 31 // bit dts as it uses an int in kernel. if (dts == DVD_NOPTS_VALUE) @@ -1801,28 +1872,22 @@ int CAMLCodec::Decode(uint8_t *pData, size_t iSize, double dts, double pts) if (iSize < 20) target_timesize = 2.0; + int rtn = 0; + // keep hw buffered demux above 1 second - if (GetTimeSize() < target_timesize && m_speed == DVD_PLAYSPEED_NORMAL) - return VC_BUFFER; + if (GetTimeSize() < target_timesize) + rtn |= VC_BUFFER; // wait until we get a new frame or 25ms, - if (m_old_pictcnt == m_cur_pictcnt) + if (m_ptsQueue.size() == 0) m_ready_event.WaitMSec(25); - // we must return VC_BUFFER or VC_PICTURE, - // default to VC_BUFFER. - int rtn = VC_BUFFER; - m_player_pts = DVD_NOPTS_VALUE; - if (m_old_pictcnt != m_cur_pictcnt) + if (m_ptsQueue.size() > 0) { - m_old_pictcnt++; - rtn = VC_PICTURE; - m_player_pts = pts; - // we got a new pict, try and keep hw buffered demux above 2 seconds. - // this, combined with the above 1 second check, keeps hw buffered demux between 1 and 2 seconds. - // we also check to make sure we keep from filling hw buffer. - if (GetTimeSize() < 2.0 && GetDataSize() < m_vbufsize/3) - rtn |= VC_BUFFER; + CSingleLock lock(m_ptsQueueMutex); + m_cur_pts = m_ptsQueue.front(); + m_ptsQueue.pop_front(); + rtn |= VC_PICTURE; } /* CLog::Log(LOGDEBUG, "CAMLCodec::Decode: " @@ -1832,6 +1897,36 @@ int CAMLCodec::Decode(uint8_t *pData, size_t iSize, double dts, double pts) return rtn; } +int CAMLCodec::DequeueBuffer(int &pts) +{ + v4l2_buffer vbuf = { 0 }; + vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (m_amlVideoFile->IOControl(VIDIOC_DQBUF, &vbuf) < 0) + { + if (errno != EAGAIN) + CLog::Log(LOGERROR, "CAMLCodec::DequeueBuffer - VIDIOC_DQBUF failed: %s", strerror(errno)); + return -errno; + } + + // Since kernel 3.14 Amlogic changed length and units of PTS values reported here. + // To differentiate such PTS values we check for existence of omx_pts_interval_lower + // parameter, because it was introduced since kernel 3.14. + if (access("/sys/module/amvideo/parameters/omx_pts_interval_lower", F_OK) != -1) + { + int64_t pts64 = vbuf.timestamp.tv_sec & 0xFFFFFFFF; + pts64 <<= 32; + pts64 += vbuf.timestamp.tv_usec & 0xFFFFFFFF; + pts = (int)((pts64 * PTS_FREQ) / DVD_TIME_BASE); + } + else + { + pts = vbuf.timestamp.tv_usec; + } + + return 0; +} + bool CAMLCodec::GetPicture(DVDVideoPicture *pDvdVideoPicture) { if (!m_opened) @@ -1843,7 +1938,7 @@ bool CAMLCodec::GetPicture(DVDVideoPicture *pDvdVideoPicture) pDvdVideoPicture->dts = DVD_NOPTS_VALUE; if (m_speed == DVD_PLAYSPEED_NORMAL) - pDvdVideoPicture->pts = m_player_pts; + pDvdVideoPicture->pts = (double)m_cur_pts / PTS_FREQ * DVD_TIME_BASE; else { if (m_cur_pts == 0) @@ -1926,40 +2021,25 @@ void CAMLCodec::Process() { CLog::Log(LOGDEBUG, "CAMLCodec::Process Started"); - // bump our priority to be level with SoftAE - SetPriority(THREAD_PRIORITY_ABOVE_NORMAL); while (!m_bStop) { - int64_t pts_video = 0; - if (am_private->am_pkt.lastpts > 0) + if (m_dll->codec_poll_cntl(&am_private->vcodec) < 0) { - // this is a blocking poll that returns every vsync. - // since we are running at a higher priority, make sure - // we sleep if the call fails or does a timeout. - if (m_dll->codec_poll_cntl(&am_private->vcodec) < 0) - { - CLog::Log(LOGDEBUG, "CAMLCodec::Process: codec_poll_cntl failed"); - Sleep(10); - } + CLog::Log(LOGDEBUG, "CAMLCodec::Process: codec_poll_cntl failed"); + Sleep(10); + } - pts_video = get_pts_video(); - if (m_cur_pts != pts_video) + { + CSingleLock lock(m_ptsQueueMutex); + int pts = 0; + if (DequeueBuffer(pts) == 0) { - //CLog::Log(LOGDEBUG, "CAMLCodec::Process: pts_video(%lld), pts_video/PTS_FREQ(%f), duration(%f)", - // pts_video, (double)pts_video/PTS_FREQ, 1.0/((double)(pts_video - m_cur_pts)/PTS_FREQ)); - - // other threads look at these, do them first - m_cur_pts = pts_video; - m_cur_pictcnt++; + m_ptsQueue.push_back(pts); m_ready_event.Set(); } } - else - { - Sleep(100); - } } - SetPriority(THREAD_PRIORITY_NORMAL); + CLog::Log(LOGDEBUG, "CAMLCodec::Process Stopped"); } diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/AMLCodec.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/AMLCodec.h index 5b9ee4f872f5e..813b913f79b08 100644 --- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/AMLCodec.h +++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/AMLCodec.h @@ -25,11 +25,15 @@ #include "guilib/Geometry.h" #include "rendering/RenderSystem.h" #include "threads/Thread.h" +#include typedef struct am_private_t am_private_t; class DllLibAmCodec; +class PosixFile; +typedef std::shared_ptr PosixFilePtr; + class CAMLCodec : public CThread { public: @@ -47,6 +51,7 @@ class CAMLCodec : public CThread int GetDataSize(); double GetTimeSize(); void SetVideoRect(const CRect &SrcRect, const CRect &DestRect); + int64_t GetCurPts() const { return m_cur_pts; } protected: virtual void Process(); @@ -60,6 +65,11 @@ class CAMLCodec : public CThread void SetVideoSaturation(const int saturation); void SetVideo3dMode(const int mode3d); std::string GetStereoMode(); + bool OpenAmlVideo(const CDVDStreamInfo &hints); + void CloseAmlVideo(); + std::string GetVfmMap(const std::string &name); + void SetVfmMap(const std::string &name, const std::string &map); + int DequeueBuffer(int &pts); DllLibAmCodec *m_dll; bool m_opened; @@ -68,8 +78,6 @@ class CAMLCodec : public CThread volatile int m_speed; volatile int64_t m_1st_pts; volatile int64_t m_cur_pts; - volatile int64_t m_cur_pictcnt; - volatile int64_t m_old_pictcnt; volatile double m_timesize; volatile int64_t m_vbufsize; int64_t m_start_dts; @@ -86,5 +94,8 @@ class CAMLCodec : public CThread int m_contrast; int m_brightness; - double m_player_pts; + PosixFilePtr m_amlVideoFile; + std::string m_defaultVfmMap; + std::deque m_ptsQueue; + CCriticalSection m_ptsQueueMutex; }; diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAmlogic.cpp b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAmlogic.cpp index 1678beda3a4a7..80f25dd664332 100644 --- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAmlogic.cpp +++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAmlogic.cpp @@ -294,7 +294,7 @@ bool CDVDVideoCodecAmlogic::GetPicture(DVDVideoPicture* pDvdVideoPicture) m_Codec->GetPicture(&m_videobuffer); *pDvdVideoPicture = m_videobuffer; - CDVDAmlogicInfo* info = new CDVDAmlogicInfo(this, m_Codec); + CDVDAmlogicInfo* info = new CDVDAmlogicInfo(this, m_Codec, (int)m_Codec->GetCurPts()); { CSingleLock lock(m_secure); @@ -573,10 +573,11 @@ void CDVDVideoCodecAmlogic::RemoveInfo(CDVDAmlogicInfo *info) m_inflight.erase(m_inflight.find(info)); } -CDVDAmlogicInfo::CDVDAmlogicInfo(CDVDVideoCodecAmlogic *codec, CAMLCodec *amlcodec) +CDVDAmlogicInfo::CDVDAmlogicInfo(CDVDVideoCodecAmlogic *codec, CAMLCodec *amlcodec, int omxPts) : m_refs(0) , m_codec(codec) , m_amlCodec(amlcodec) + , m_omxPts(omxPts) { } diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAmlogic.h b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAmlogic.h index c15238b6b3f82..a4cc25ce51b09 100644 --- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAmlogic.h +++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/DVDVideoCodecAmlogic.h @@ -36,13 +36,14 @@ class CDVDVideoCodecAmlogic; class CDVDAmlogicInfo { public: - CDVDAmlogicInfo(CDVDVideoCodecAmlogic *codec, CAMLCodec *amlcodec); + CDVDAmlogicInfo(CDVDVideoCodecAmlogic *codec, CAMLCodec *amlcodec, int omxPts); // reference counting CDVDAmlogicInfo* Retain(); long Release(); CAMLCodec *getAmlCodec() const; + int GetOmxPts() const { return m_omxPts; } void invalidate(); protected: @@ -51,6 +52,7 @@ class CDVDAmlogicInfo CDVDVideoCodecAmlogic* m_codec; CAMLCodec* m_amlCodec; + int m_omxPts; }; class CDVDVideoCodecAmlogic : public CDVDVideoCodec diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererAML.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererAML.cpp index f6b7bb1daa0a7..d0baa27dd358a 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererAML.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererAML.cpp @@ -27,18 +27,18 @@ #include "cores/VideoPlayer/DVDCodecs/Video/AMLCodec.h" #include "utils/log.h" #include "utils/GLUtils.h" +#include "utils/SysfsUtils.h" #include "settings/MediaSettings.h" #include "windowing/WindowingFactory.h" #include "cores/VideoPlayer/VideoRenderers/RenderCapture.h" CRendererAML::CRendererAML() { - + m_prevPts = -1; } CRendererAML::~CRendererAML() { - } bool CRendererAML::RenderCapture(CRenderCapture* capture) @@ -118,7 +118,7 @@ bool CRendererAML::LoadShadersHook() { CLog::Log(LOGNOTICE, "GL: Using AML render method"); m_textureTarget = GL_TEXTURE_2D; - m_renderMethod = RENDER_FMT_AML; + m_renderMethod = RENDER_BYPASS; return false; } @@ -132,15 +132,19 @@ bool CRendererAML::RenderUpdateVideoHook(bool clear, DWORD flags, DWORD alpha) ManageRenderArea(); CDVDAmlogicInfo *amli = static_cast(m_buffers[m_iYV12RenderBuffer].hwDec); - if (amli) + if (amli && amli->GetOmxPts() != m_prevPts) { + m_prevPts = amli->GetOmxPts(); + SysfsUtils::SetInt("/sys/module/amvideo/parameters/omx_pts", amli->GetOmxPts()); + CAMLCodec *amlcodec = amli->getAmlCodec(); if (amlcodec) amlcodec->SetVideoRect(m_sourceRect, m_destRect); } + usleep(10000); + return true; } #endif - diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererAML.h b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererAML.h index deea29b746f68..819f6e3048c67 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererAML.h +++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererAML.h @@ -55,6 +55,9 @@ class CRendererAML : public CLinuxRendererGLES virtual bool RenderHook(int index); virtual int GetImageHook(YV12Image *image, int source = AUTOSOURCE, bool readonly = false); virtual bool RenderUpdateVideoHook(bool clear, DWORD flags = 0, DWORD alpha = 255); + +private: + int m_prevPts; }; #endif diff --git a/xbmc/utils/AMLUtils.cpp b/xbmc/utils/AMLUtils.cpp index 5915e613d5d54..4ae45f343ea6e 100644 --- a/xbmc/utils/AMLUtils.cpp +++ b/xbmc/utils/AMLUtils.cpp @@ -118,6 +118,21 @@ bool aml_permissions() CLog::Log(LOGERROR, "AML: no rw on /sys/class/tsync/pts_pcrscr"); permissions_ok = 0; } + if (!SysfsUtils::HasRW("/dev/video10")) + { + CLog::Log(LOGERROR, "AML: no rw on /dev/video10"); + permissions_ok = 0; + } + if (!SysfsUtils::HasRW("/sys/module/amvideo/parameters/omx_pts")) + { + CLog::Log(LOGERROR, "AML: no rw on /sys/module/amvideo/parameters/omx_pts"); + permissions_ok = 0; + } + if (!SysfsUtils::HasRW("/sys/module/amlvideodri/parameters/freerun_mode")) + { + CLog::Log(LOGERROR, "AML: no rw on /sys/module/amlvideodri/parameters/freerun_mode"); + permissions_ok = 0; + } if (!SysfsUtils::HasRW("/sys/class/audiodsp/digital_raw")) { CLog::Log(LOGERROR, "AML: no rw on /sys/class/audiodsp/digital_raw");