Skip to content
This repository

Add buffering to video renderers (without dropping) #2309

Closed
wants to merge 19 commits into from

6 participants

Rainer Hochecker Joakim Plate rbej popcornmix Cory Fields jmarshallnz
Rainer Hochecker
Collaborator

On request by elupus this is #2249 without lateness detection and dropping control in dvdplayer. It's more convenient for review but I don't recommend to test this pr alone because the old lateness detection won't work well with buffering.

@bobo1on1
Do you have a good idea how to calculate absolute timestamps in advance without breaking "sync playback to display"? In this pr I have moved this code from dvdplayer to rendermanager. This way it works the same way as it did before but it requires RenderManager to have access to dvdclock.

Joakim Plate
Collaborator
Joakim Plate
Collaborator
Rainer Hochecker
Collaborator

Ok, I have removed the clock from RenderManager, it uses absolute timestamps now.

xbmc/cores/VideoRenderers/RenderManager.cpp
((156 lines not shown))
  1093
+{
  1094
+  CRetakeLock<CExclusiveLock> lock(m_sharedSection);
  1095
+  if (!m_pRenderer)
  1096
+    return;
  1097
+
  1098
+  if (m_iNumRenderBuffers >= 3)
  1099
+  {
  1100
+    int last = m_iDisplayedRenderBuffer;
  1101
+    m_iDisplayedRenderBuffer = (m_iCurrentRenderBuffer + m_iNumRenderBuffers - 1) % m_iNumRenderBuffers;
  1102
+
  1103
+    if (last != m_iDisplayedRenderBuffer
  1104
+        && m_iDisplayedRenderBuffer != m_iCurrentRenderBuffer)
  1105
+    {
  1106
+      m_pRenderer->ReleaseBuffer(m_iDisplayedRenderBuffer);
  1107
+      if (m_overlays.ReleaseBuffer(m_iDisplayedRenderBuffer) > 0)
  1108
+        m_bOverlayReleased = true;
6
Joakim Plate Collaborator
elupus added a note

You should not rely on the return value of release. The normal case for overlays is that the same overlay can be queued up for multiple frames. This there is no guarantee that the overlay ever get released. It is just a matter how how long it should be on screen.

Rainer Hochecker Collaborator

I try to describe the problem, you may have a better idea:

The bypass method e.g. used on the PI works as follows. video player never calls AddVideoPicture and AddOverlay only if one has to be processed. FlipPage is called on every cycle in order to keep the GUI responsive. FlipPage won't consume a buffer if nothing was added. The problem was when a subtitle was on screen and subtitles have been disabled in the GUI, the subtitle did not disappear. This has solved the issue. Setting this flag makes FlipPage consume a buffer and the sub can be cleared on next cycle.

Joakim Plate Collaborator
elupus added a note

Why can't FlipPage always consume a buffer? Can't see why that would be a problem.

Rainer Hochecker Collaborator

That's an optimization for the bypass method e.g. used by RPi. If it does not add anything, it does not have to wait for a free buffer and can continue decoding and filling the fifo buffer which is a different layer for rendering video. That's a huge performance gain on the RPi.

Joakim Plate Collaborator
elupus added a note
Rainer Hochecker Collaborator

We achieved best results by calling WaitForBuffer with a timeout of zero and continue processing packets in case there is no free buffer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
xbmc/cores/VideoRenderers/RenderManager.cpp
((103 lines not shown))
  1040
+}
  1041
+
  1042
+void CXBMCRenderManager::PrepareNextRender()
  1043
+{
  1044
+  int idx = GetNextRenderBufferIndex();
  1045
+  if (idx < 0)
  1046
+  {
  1047
+    if (m_speed >= DVD_PLAYSPEED_NORMAL && g_graphicsContext.IsFullScreenVideo())
  1048
+      CLog::Log(LOGDEBUG,"%s no buffer, out: %d, current: %d, display: %d",
  1049
+        __FUNCTION__, m_iOutputRenderBuffer, m_iCurrentRenderBuffer, m_iDisplayedRenderBuffer);
  1050
+    return;
  1051
+  }
  1052
+
  1053
+  double presenttime = m_renderBuffers[idx].timestamp;
  1054
+  double clocktime = GetPresentTime();
  1055
+  if(presenttime - clocktime > MAXPRESENTDELAY)
3
Joakim Plate Collaborator
elupus added a note

This logic should be improved. It should find the last frame in the queue for which the time has passed or if non exist, the first in queue.

ie if clock is 5

If queue looks like:
2 <-- this should be released
4 <-- this should be selected
6
8

If queue looks like:
6 <-- this should be selected
8
10

That way it will auto drop really late frames.

Rainer Hochecker Collaborator

If you don't mind I would like to keep it simple here until we look at the dropping. Dropping frames here and in video player may interfere.
My thoughts: If a frame has made it to the render buffers we already put a lot of effort in to get it to this stage. So why not display it? It does not harm being slightly late for a couple of frames as long as we catch up and make a drop hardly noticeable to the user.

Joakim Plate Collaborator
elupus added a note

Well, for 24fps content on 24hz display that assumption doesn't hold. We must drop frames on output there. With the change I suggested this pull becomes self sustaining. We should obviously look at the dropping code more in detail too, but getting this to work by itself would be nice too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
xbmc/cores/VideoRenderers/RenderManager.cpp
... ...
@@ -904,3 +938,188 @@ EINTERLACEMETHOD CXBMCRenderManager::AutoInterlaceMethodInternal(EINTERLACEMETHO
904 938
 
905 939
   return mInt;
906 940
 }
  941
+
  942
+int CXBMCRenderManager::WaitForBuffer(volatile bool& bStop, int timeout)
  943
+{
  944
+  CSharedLock lock(m_sharedSection);
  945
+  if (!m_pRenderer)
  946
+    return -1;
  947
+
  948
+  double maxwait = GetPresentTime() + (float)timeout/1000;
1
Joakim Plate Collaborator
elupus added a note

Just use the normal EndTime timers here. This is not timing critical.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Joakim Plate elupus commented on the diff
xbmc/cores/VideoRenderers/RenderManager.cpp
... ...
@@ -830,25 +856,33 @@ int CXBMCRenderManager::AddVideoPicture(DVDVideoPicture& pic)
830 856
   }
831 857
 #ifdef HAVE_LIBVDPAU
832 858
   else if(pic.format == RENDER_FMT_VDPAU)
833  
-    m_pRenderer->AddProcessor(pic.vdpau);
  859
+    m_pRenderer->AddProcessor(pic.vdpau, index);
2
Joakim Plate Collaborator
elupus added a note

This index aught to be the same as the index above right? So we probably should just set up the index var first and ignore return from GetImage()

Rainer Hochecker Collaborator

yep, that makes it clearer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
xbmc/cores/VideoRenderers/RenderManager.cpp
... ...
@@ -534,25 +549,21 @@ void CXBMCRenderManager::SetViewMode(int iViewMode)
534 549
     m_pRenderer->SetViewMode(iViewMode);
535 550
 }
536 551
 
537  
-void CXBMCRenderManager::FlipPage(volatile bool& bStop, double timestamp /* = 0LL*/, int source /*= -1*/, EFIELDSYNC sync /*= FS_NONE*/)
  552
+void CXBMCRenderManager::FlipPage(volatile bool& bStop, double timestamp /* = 0LL*/, double pts /* = 0 */, int source /*= -1*/, EFIELDSYNC sync /*= FS_NONE*/, int speed /*= 0*/)
538 553
 {
2
Joakim Plate Collaborator
elupus added a note

Unrelated change now that we use abs time, pts is not required here (event thou it might be nicer from a overlay renderer perspective to pass it here instead of in AddOverlay()). Let's keep that out of this change thou.

Rainer Hochecker Collaborator

I'll remove it from this pr though I still need it for lateness detection. Let's discuss on the other pr.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Joakim Plate
Collaborator

Looks quite nice. If we set buffering to true and only allow 2 buffers, I assume it will work almost identical to old code? If so i think we should avoid the SetBuffering() function and just have the Configure call configure queue length too.

Rainer Hochecker
Collaborator

If renderer provides less than 3 render buffers, buffering can't be enabled. I have tested this on Windows before I added more buffers to WinRenderer. Yes it should work identical to old code.
Even it buffering is supported by renderer and enabled, player can enable/disable it with a call to EnableBuffering. I noticed that buffering is not ideal when e.g. going ff or rw.

Configure call configure queue length too

The length of the queue is hard coded to 5 in ResetRenderBuffers which is called by Configure. Do you want the size as an additional parameter to Configure?

Joakim Plate
Collaborator

Leave the enabledisable call for now, and ignore the queue length. Keep in mind that i want to merge elupus@306a028 at some point.

That is this pull taken one step further. Queuing done by windowing system.

Rainer Hochecker
Collaborator
  • very late frames in the buffer are skipped now. I need to test this on the other branch where buffering is enabled
  • enable/disable calls removed from player. buffering is enabled in configure if requested
  • I had m_speed as a parameter in FlipPage. I think it's better to notify renderManager about speed by calling a separate method: SetSpeed
Rainer Hochecker
Collaborator

@elupus, does this have a chance to get merged in the current window?

Joakim Plate
Collaborator
Joakim Plate
Collaborator
rbej
rbej commented

New version "OMXPlayer: adapt to buffering" reduces too much performance on Rpi. Merged subs in mkv now is perfect sync, but performace is too much down. Old version have "normal" performance but have little out of sync merged subs. I try "OMXPlayerVideo: try different timing " and nothing change.

Rainer Hochecker
Collaborator

you need this on top: FernetMenta@07e2468

rbej
rbej commented

With "enable dirty regions for video full screen" performance is still down on Rpi. This same with PR 2556.

Rainer Hochecker
Collaborator

you are talking about performance when subtitles are displayed, right? The "problem" that subs are rendered at fps rate. For systems having separate video layers like the pi this is not necessary. We would gain performance if we only rendered subs if they had changed.

rbej
rbej commented

Im talking about stuttering when play high bitrate mkv movies. Old version no have stutters on Rpi.

popcornmix
Collaborator

@rbej
Is the performance worse for:
1) videos with no subs.
2) videos with subs when disabled
3) video with subs that are enabled
?

rbej
rbej commented

Overall performance. With and without subs.

This version "OMX Player" adapt to buffering working good, without performance drop.

http://pastebin.com/b4vDQT1j

Rainer Hochecker
Collaborator

@rbej @popcornmix could you try #2556 if performance is better? I did some changes. 2556 is this pr + "enable dirty regions for full scren"

rbej
rbej commented

No change. High gui fps (25-30) when play high bitrate mkv causes too much cpu load (100%) and stutters. If gui fps is low (9-15) everything is ok and no stutters. Stutters in only when fps gui is high.

Rainer Hochecker
Collaborator

how do you observe high gui fps without bringing up the codec screen? The codec screen and debug info have much impact on performance. Note that the old code does not drive the gui, this is why subs are out of sync. #2556 only renders the gui when something has changed.

Cory Fields
Owner
theuni commented

@FernetMenta We really need to sync up. Going forward, everything you're doing and what's going on in https://github.com/theuni/xbmc/tree/rendermanager-master will need to play nice together.

Cory Fields
Owner
theuni commented

@FernetMenta many (most?) GL implementations will block on any draw call after the 2nd buffer is queued for swap. I'm not sure what benefit we get from queuing beyond that, but I'm also having a hard time reading through these changes at a conceptual level.

Mind doing up a quick description of the general flow of things before/after these changes?

Rainer Hochecker
Collaborator

@theuni good idea, lets sync. I will write some infos about this together.

in short:

many (most?) GL implementations will block on any draw call after the 2nd buffer is queued for swap

exactly this is the problem. Currently player submits a frame to rederer and waits until it's picked up an redered. Precious decoding and post-processing time is wasted. With this pr player submits a frame to rederer which queues it up and returns immediately to player which can decode the frame.

Rainer Hochecker
Collaborator

current:

  • video player decodes a frame
  • the same thread does post processing, i.e. deinterlacing
  • video player tries to deliver frame to rendere: RenderManager.AddVideoPicture (it may block here waiting one of the two buffers)
  • video player calls RenderManager.FlipPage
  • it waits for RenderManager being idle
  • RenderManager is preparing for current present method
  • still FlipPage: it waits for the main thread to step by and flip render buffers here we have already an issue. two buffers are not enough. gl allows going one frame ahead and the buffer which is marked for reuse here may not have been rendered (to display) at this time. This leads to video curruption due to buffer overwrite when using hw decoders like vdpau
  • the main thread flips render buffers and sets a flag
  • main thread starts rendering the frame
  • video player thread returns to decoding after m_presentstep == PRESENT_FLIP I have measured quite long delays for the video player thread waiting here, up to 40ms running 50Hz (main thread stuck in glxSwapBuffers)
  • main thread renders gui after having rendered video this can make video thread wait even longer on the next cycle, noticeable when binging up the info or some other dialog -> stuttering and droppped frames.

Waiting too long on the hand shake of video player and RenderManager results in a dropped frame. As I have started
with XBMC I had approx 250 dropped farmes an hour (1080i50 vdpau). Now I have zero.

after:

  • vidoe player decodes, post processing
  • video player waits for a free render buffer (currently 5)
  • after a buffer is available video player submits a frame to RenderManager by calling FlipPage
  • the call returns immdiately and video player can continue decoding
  • main thread asks RenderManager if it has a frame (event, no polling)
  • if so it calls RenderManager.Present()
  • it takes a queued frame out of the queue and renders it
  • two flips (swapBuffers) after rendering we know that the frame has made it to display and this buffer is marked for reuse

A decoding cycle can even take more than 20 ms on 50Hz without causing a drop. Decoding and rendering, which is asynchronous
anyway can go their own speed. The handshake is removed.
I have analyzed every single frame drop over months and built dozens of prototypes. This turned out to be the best solution.

Cory Fields
Owner
theuni commented

Grr, I had a long response typed up then closed the tab :\

Basically, I think that you and I are working on similar things, and implementing them in ways that conflict with eachother. I had planned on taking on video rendering as a follow-up to the compositing work in #2681 . Not trying to dismiss the work being done here by any means, I'd just like to be sure we're solving the right problem.

It might be helpful to hear from @jmarshallnz and/or @elupus about the different approaches. I'm of the opinion that the only way we'll ever get reliable (smooth) video playback is through an off-thread compositing pipeline design that takes the GUI fully into account, so the locks and events here are quite scary imo.

Rainer Hochecker
Collaborator

@theuni you are very welcome considering an interface in your module which fits the needs of video player. not an easy task. with the current code hw decoders require to "hijack" the render thread to do interop work between decoded video surfaces and the gl. my new implementation for vdpau (and xvba) deliver ready to render textures which makes life easier for the renderer. the textures are owned by the decoders, hence they need to flow back after they have been rendered to display. (a vdpau video surface is actually the texture)

How about your priorities and when do you plan to follow-up on video? Note that this work is already enjoyed by masses of users. It has been in OE for more than a year because it makes video playback much better to what we have currently in mainline. I don't see any major flaws with the current gui.

Cory Fields
Owner
theuni commented

@FernetMenta You're right, and I agree that if it's an improvement it should go in. My concern is that if there's major refactor work going on, I'd like us to coordinate rather than go in separate directions.

My timeline mainly revolves around when someone might be able to take a crack at the directx side of the render manager. I've charged on with new stuff (rendering and windowing have been decoupled, gl now renders in its own thread rather than app thread) that make the pipeline easier to use, but the first parts of it won't go in any sooner than next merge window.

jmarshallnz
Owner

@elupus: current merge window closing soon. Will you have time to look at it over the next week? If so, I suggest holding off until reviewed and slot it in once ready. If not, perhaps we should consider merging and fixing up any changes as needed in order to not delay further work in this area?

Rainer Hochecker
Collaborator

@theuni I don't claim that this is final, like you said it's an improvement. The point is that all my work on Linux video improvements, vdpau and xvba, is based on this. Without this layer has been improved vdpau and xvba won't work as good as on my master branch which has diverged by 11k loc. (refactoring like in this merge window is hell for me :)
I can't support those changes on different branches.

Joakim Plate
Collaborator
elupus commented

I suspect i won't have time. It's too large to get a quick grip of. And I think it is a bit messy.

jmarshallnz
Owner

When will you have time? We can't afford to leave something like this for much longer when it's holding up further development.

Would you be OK with it being merged and (re)worked as necessary over the coming months as you get a chance to review carefully and see it in practice?

Joakim Plate
Collaborator
elupus commented
Cory Fields

Are this many really necessary? Does that affect memory consumption as it would imply?

Collaborator

This is just the maximum, the actual number of buffers are requested by CXBMCRenderManager::ResetRenderBuffer(). Currently hardcoded to 5 in case buffering is supported and requested by render method

Cory Fields

use some define or constant?

Collaborator

6e80e5e
this pr has had some sporadic reviews and I did not squash commits to show progress.

Cory Fields
Collaborator

Only called from CXBMCRenderManager::FlipPage where the lock is already grabbed.

Cory Fields
Owner
theuni commented

@FernetMenta At a conceptual level: If we're going to buffer 5 full frames, why are we not storing them on the gpu? Seems like we're making some big changes to reduce stalls without addressing one of the major causes.

Also, it's worth noting that different environments will block at different stages in the pipeline. Yours blocks at glxSwapBuffers, but others may block in draw or clear functions. Also, double/triple/quad buffering drivers will change the results drastically. When triple/quad buffering, 2 flips may not indicate that the frame has been displayed, since they may be dropped behind your back. In these cases, vsync tends to be a better metric. Has this been tested on drivers that operate that way?

Rainer Hochecker
Collaborator

@theuni

why are we not storing them on the gpu

This is the case and this pr did not change anything to it. When using hw decoders the video surfaces won't leave the GPU. For sw decoding the video frames are buffered in PBOs.

Yours blocks at glxSwapBuffers

For fullsceen video we don't block in glxSwapBuffers. We wait in RenderManager::WaitPresentTime. This hasn't changed either. It may block temporarily in swapBuffers but then return to normal flow.

When triple/quad buffering

completely agree that this can be an issue. It also has impact on a/v sync.

Rainer Hochecker
Collaborator

I don't really want to rush this in when key persons are not ready. I am happy if this gets attention and we discuss the requirements at a conceptual level before looking into the details.

At a conceptual level this pr does not change too much, in this iteration it actually doesn't change anything to behavior. None of the render formats requests buffering: FernetMenta@bfb9177
Buffering just extends what we have now, two buffers to a higher number. This allows removing the sync point between decoder thread and render thread.
Doing this we are facing the next problem: video player detects lateness at output stage of player and drops frames according to this. Dropping a frame in the middle of the queue does not look smooth. Lateness has to be detected at the end of the queue, then player has to decide for a method that results in a not very noticeable effect to the user. It's much better to skip just a deinterlacing cycle than to drop a frame in decoder which would also disrupt the next deinterlacing cycles when using advanced methods like temporal/spacial. It may take some time for this saving of time, of a dropped frame or deinterlacing cycle, to show up at the end of the queue. This has to be taken into account. The current implementation just drops another frame on the next cycle if still late.
Originally FernetMenta@43906aa was part of this pr but I split on request.

Joakim Plate elupus referenced this pull request
Closed

RenderManager refactoring #2733

Joakim Plate
Collaborator
elupus commented

I pushed a rebased version on top of my rendermanager changes here: https://github.com/elupus/xbmc/tree/renderer untested. Turns out it wasn't hard to adapt.

Rainer Hochecker
Collaborator

cool, thanks!

Joakim Plate
Collaborator
elupus commented

Seems i posted the wrong url: https://github.com/elupus/xbmc/tree/renderbuf and https://github.com/elupus/xbmc/compare/renderbuf

Enough for today. Let's hope I can get some more time to work on it again. There is one thing sorely missing now. Buffers that are queued up are not uploaded to GPU until right before render (ie as it was before). PBO's must be released for them to be uploaded to GPU.

I have likely broken stuff but seem to work for me. There are some issues still with DiscardBuffer() which I just noticed. They need to release buffers not only re-point the indexes. But that is from wrong thread so it's not so easy.

Joakim Plate
Collaborator
elupus commented

Also forgot to mention. Buffering is still disabled in my branch. Needs enabling in DVDPlayerVideo.cpp GetBufferingEnabled() to get going.

Rainer Hochecker
Collaborator

Buffers that are queued up are not uploaded to GPU until right before render (ie as it was before). PBO's must be released for them to be uploaded to GPU.

I think in order to see real benefit uploading has to be offloaded from render thread. Some time ago I had a prototype for this. RenderManager could own a thread which shares the gl context with the render thread. Then it also could do preparation of overlays which can be time consuming as well.

There are some issues still with DiscardBuffer() which I just noticed. They need to release buffers not only re-point the indexes

Because of the state bound/unbound of the PBOs, right? I struggled with state on a different part of the code. You remember we were discussing about dropping in the output stage. I had this in for a while but took it out again because it ruined detection of when a buffer is ready for release. Currently it is assumed that the current render buffer can be reused after two flips. Advancing the current buffer index without rendering breaks that simple logic. Maybe every buffer should carry its own state information and picking a free buffer should be based on this state, not on a ring buffer logic.

Joakim Plate
Collaborator
elupus commented
Cory Fields
Owner
theuni commented

@FernetMenta You've just described the rendermanager that I'm desperately trying to push us towards :) All rendering is deferred, so there is never any blocking. Fences can be added so that we get callbacks from the render api when the scene has been rendered, with no guessing.

Rainer Hochecker FernetMenta referenced this pull request in OpenELEC/OpenELEC.tv
Closed

ASIC hang back again #2296

Rainer Hochecker FernetMenta closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
53  xbmc/Application.cpp
@@ -414,8 +414,6 @@ CApplication::CApplication(void)
414 414
 #endif
415 415
   m_currentStack = new CFileItemList;
416 416
 
417  
-  m_frameCount = 0;
418  
-
419 417
   m_bPresentFrame = false;
420 418
   m_bPlatformDirectories = true;
421 419
 
@@ -2041,28 +2039,18 @@ float CApplication::GetDimScreenSaverLevel() const
2041 2039
 
2042 2040
 bool CApplication::WaitFrame(unsigned int timeout)
2043 2041
 {
2044  
-  bool done = false;
2045  
-
2046 2042
   // Wait for all other frames to be presented
2047  
-  CSingleLock lock(m_frameMutex);
2048  
-  //wait until event is set, but modify remaining time
  2043
+  m_frameEvent.Reset();
2049 2044
 
2050  
-  TightConditionVariable<InversePredicate<int&> > cv(m_frameCond, InversePredicate<int&>(m_frameCount));
2051  
-  cv.wait(lock,timeout);
2052  
-  done = m_frameCount == 0;
  2045
+  if (!g_renderManager.HasFrame() && !m_frameEvent.WaitMSec(timeout))
  2046
+    return false;
2053 2047
 
2054  
-  return done;
  2048
+  return g_renderManager.HasFrame();
2055 2049
 }
2056 2050
 
2057 2051
 void CApplication::NewFrame()
2058 2052
 {
2059  
-  // We just posted another frame. Keep track and notify.
2060  
-  {
2061  
-    CSingleLock lock(m_frameMutex);
2062  
-    m_frameCount++;
2063  
-  }
2064  
-
2065  
-  m_frameCond.notifyAll();
  2053
+  m_frameEvent.Set();
2066 2054
 }
2067 2055
 
2068 2056
 void CApplication::Render()
@@ -2075,7 +2063,6 @@ void CApplication::Render()
2075 2063
 
2076 2064
   int vsync_mode = CSettings::Get().GetInt("videoscreen.vsync");
2077 2065
 
2078  
-  bool decrement = false;
2079 2066
   bool hasRendered = false;
2080 2067
   bool limitFrames = false;
2081 2068
   unsigned int singleFrameTime = 10; // default limit 100 fps
@@ -2089,13 +2076,10 @@ void CApplication::Render()
2089 2076
     m_bPresentFrame = false;
2090 2077
     if (!extPlayerActive && g_graphicsContext.IsFullScreenVideo() && !IsPaused())
2091 2078
     {
2092  
-      CSingleLock lock(m_frameMutex);
2093  
-
2094  
-      TightConditionVariable<int&> cv(m_frameCond,m_frameCount);
2095  
-      cv.wait(lock,100);
2096  
-
2097  
-      m_bPresentFrame = m_frameCount > 0;
2098  
-      decrement = m_bPresentFrame;
  2079
+      m_frameEvent.Reset();
  2080
+      m_bPresentFrame = g_renderManager.HasFrame();
  2081
+      if (!m_bPresentFrame && m_frameEvent.WaitMSec(100))
  2082
+        m_bPresentFrame = g_renderManager.HasFrame();
2099 2083
       hasRendered = true;
2100 2084
     }
2101 2085
     else
@@ -2119,8 +2103,6 @@ void CApplication::Render()
2119 2103
         else if (lowfps)
2120 2104
           singleFrameTime = 200;  // 5 fps, <=200 ms latency to wake up
2121 2105
       }
2122  
-
2123  
-      decrement = true;
2124 2106
     }
2125 2107
   }
2126 2108
 
@@ -2177,18 +2159,14 @@ void CApplication::Render()
2177 2159
   m_lastFrameTime = XbmcThreads::SystemClockMillis();
2178 2160
 
2179 2161
   if (flip)
  2162
+  {
2180 2163
     g_graphicsContext.Flip(dirtyRegions);
  2164
+    g_renderManager.NotifyDisplayFlip();
  2165
+  }
2181 2166
   CTimeUtils::UpdateFrameTime(flip);
2182 2167
 
2183 2168
   g_renderManager.UpdateResolution();
2184 2169
   g_renderManager.ManageCaptures();
2185  
-
2186  
-  {
2187  
-    CSingleLock lock(m_frameMutex);
2188  
-    if(m_frameCount > 0 && decrement)
2189  
-      m_frameCount--;
2190  
-  }
2191  
-  m_frameCond.notifyAll();
2192 2170
 }
2193 2171
 
2194 2172
 void CApplication::SetStandAlone(bool value)
@@ -5412,12 +5390,6 @@ bool CApplication::SwitchToFullScreen()
5412 5390
   // See if we're playing a video, and are in GUI mode
5413 5391
   if ( IsPlayingVideo() && g_windowManager.GetActiveWindow() != WINDOW_FULLSCREEN_VIDEO)
5414 5392
   {
5415  
-    // Reset frame count so that timing is FPS will be correct.
5416  
-    {
5417  
-      CSingleLock lock(m_frameMutex);
5418  
-      m_frameCount = 0;
5419  
-    }
5420  
-
5421 5393
     // then switch to fullscreen mode
5422 5394
     g_windowManager.ActivateWindow(WINDOW_FULLSCREEN_VIDEO);
5423 5395
     return true;
@@ -5650,7 +5622,6 @@ bool CApplication::IsCurrentThread() const
5650 5622
 
5651 5623
 bool CApplication::IsPresentFrame()
5652 5624
 {
5653  
-  CSingleLock lock(m_frameMutex);
5654 5625
   bool ret = m_bPresentFrame;
5655 5626
 
5656 5627
   return ret;
6  xbmc/Application.h
@@ -414,10 +414,8 @@ class CApplication : public CXBApplicationEx, public IPlayerCallback, public IMs
414 414
   bool m_bEnableLegacyRes;
415 415
   bool m_bTestMode;
416 416
   bool m_bSystemScreenSaverEnable;
417  
-
418  
-  int        m_frameCount;
419  
-  CCriticalSection m_frameMutex;
420  
-  XbmcThreads::ConditionVariable  m_frameCond;
  417
+  
  418
+  CEvent m_frameEvent;
421 419
 
422 420
   VIDEO::CVideoInfoScanner *m_videoInfoScanner;
423 421
   MUSIC_INFO::CMusicInfoScanner *m_musicInfoScanner;
8  xbmc/cores/VideoRenderers/BaseRenderer.h
@@ -28,12 +28,13 @@
28 28
 
29 29
 #define MAX_PLANES 3
30 30
 #define MAX_FIELDS 3
  31
+#define NUM_BUFFERS 10
31 32
 
32 33
 class CSetting;
33 34
 
34 35
 typedef struct YV12Image
35 36
 {
36  
-  BYTE *   plane[MAX_PLANES];
  37
+  uint8_t* plane[MAX_PLANES];
37 38
   int      planesize[MAX_PLANES];
38 39
   unsigned stride[MAX_PLANES];
39 40
   unsigned width;
@@ -96,10 +97,13 @@ class CBaseRenderer
96 97
   void GetVideoRect(CRect &source, CRect &dest);
97 98
   float GetAspectRatio() const;
98 99
 
99  
-  virtual bool AddVideoPicture(DVDVideoPicture* picture) { return false; }
  100
+  virtual bool AddVideoPicture(DVDVideoPicture* picture, int index) { return false; }
100 101
   virtual void Flush() {};
101 102
 
102 103
   virtual unsigned int GetProcessorSize() { return 0; }
  104
+  virtual unsigned int GetMaxBufferSize() { return 0; }
  105
+  virtual void SetBufferSize(int numBuffers) { }
  106
+  virtual void ReleaseBuffer(int idx) { }
103 107
 
104 108
   virtual bool Supports(ERENDERFEATURE feature) { return false; }
105 109
 
43  xbmc/cores/VideoRenderers/LinuxRendererGL.cpp
@@ -250,14 +250,6 @@ bool CLinuxRendererGL::ValidateRenderer()
250 250
   return true;
251 251
 }
252 252
 
253  
-
254  
-void CLinuxRendererGL::ManageTextures()
255  
-{
256  
-  m_NumYV12Buffers = 2;
257  
-  //m_iYV12RenderBuffer = 0;
258  
-  return;
259  
-}
260  
-
261 253
 bool CLinuxRendererGL::ValidateRenderTarget()
262 254
 {
263 255
   if (!m_bValidated)
@@ -274,7 +266,7 @@ bool CLinuxRendererGL::ValidateRenderTarget()
274 266
     // function pointer for texture might change in
275 267
     // call to LoadShaders
276 268
     glFinish();
277  
-    for (int i = 0 ; i < m_NumYV12Buffers ; i++)
  269
+    for (int i = 0 ; i < NUM_BUFFERS ; i++)
278 270
       (this->*m_textureDelete)(i);
279 271
 
280 272
     // trigger update of video filters
@@ -618,13 +610,28 @@ void CLinuxRendererGL::Flush()
618 610
   glFinish();
619 611
   m_bValidated = false;
620 612
   m_fbo.fbo.Cleanup();
  613
+  m_iYV12RenderBuffer = 0;
  614
+}
  615
+
  616
+void CLinuxRendererGL::ReleaseBuffer(int idx)
  617
+{
  618
+  YUVBUFFER &buf = m_buffers[idx];
  619
+#ifdef HAVE_LIBVDPAU
  620
+  SAFE_RELEASE(buf.vdpau);
  621
+#endif
  622
+#ifdef HAVE_LIBVA
  623
+  buf.vaapi.surface.reset();
  624
+#endif
  625
+#ifdef TARGET_DARWIN
  626
+  if (buf.cvBufferRef)
  627
+    CVBufferRelease(buf.cvBufferRef);
  628
+#endif
621 629
 }
622 630
 
623 631
 void CLinuxRendererGL::Update(bool bPauseDrawing)
624 632
 {
625 633
   if (!m_bConfigured) return;
626 634
   ManageDisplay();
627  
-  ManageTextures();
628 635
   m_scalingMethodGui = (ESCALINGMETHOD)-1;
629 636
 }
630 637
 
@@ -641,7 +648,6 @@ void CLinuxRendererGL::RenderUpdate(bool clear, DWORD flags, DWORD alpha)
641 648
   }
642 649
 
643 650
   ManageDisplay();
644  
-  ManageTextures();
645 651
 
646 652
   g_graphicsContext.BeginPaint();
647 653
 
@@ -798,7 +804,6 @@ unsigned int CLinuxRendererGL::PreInit()
798 804
     m_resolution = RES_DESKTOP;
799 805
 
800 806
   m_iYV12RenderBuffer = 0;
801  
-  m_NumYV12Buffers = 2;
802 807
 
803 808
   m_formats.push_back(RENDER_FMT_YUV420P);
804 809
   GLint size;
@@ -2481,7 +2486,7 @@ void CLinuxRendererGL::UploadVAAPITexture(int index)
2481 2486
   || status == VA_STATUS_ERROR_INVALID_DISPLAY)
2482 2487
   {
2483 2488
     va.display->lost(true);
2484  
-    for(int i = 0; i < NUM_BUFFERS; i++)
  2489
+    for(int i = 0; i < m_NumYV12Buffers; i++)
2485 2490
     {
2486 2491
       m_buffers[i].vaapi.display.reset();
2487 2492
       m_buffers[i].vaapi.surface.reset();
@@ -3435,26 +3440,26 @@ void CLinuxRendererGL::UnBindPbo(YUVBUFFER& buff)
3435 3440
 }
3436 3441
 
3437 3442
 #ifdef HAVE_LIBVDPAU
3438  
-void CLinuxRendererGL::AddProcessor(CVDPAU* vdpau)
  3443
+void CLinuxRendererGL::AddProcessor(CVDPAU* vdpau, int index)
3439 3444
 {
3440  
-  YUVBUFFER &buf = m_buffers[NextYV12Texture()];
  3445
+  YUVBUFFER &buf = m_buffers[index];
3441 3446
   SAFE_RELEASE(buf.vdpau);
3442 3447
   buf.vdpau = (CVDPAU*)vdpau->Acquire();
3443 3448
 }
3444 3449
 #endif
3445 3450
 
3446 3451
 #ifdef HAVE_LIBVA
3447  
-void CLinuxRendererGL::AddProcessor(VAAPI::CHolder& holder)
  3452
+void CLinuxRendererGL::AddProcessor(VAAPI::CHolder& holder, int index)
3448 3453
 {
3449  
-  YUVBUFFER &buf = m_buffers[NextYV12Texture()];
  3454
+  YUVBUFFER &buf = m_buffers[index];
3450 3455
   buf.vaapi.surface = holder.surface;
3451 3456
 }
3452 3457
 #endif
3453 3458
 
3454 3459
 #ifdef TARGET_DARWIN
3455  
-void CLinuxRendererGL::AddProcessor(struct __CVBuffer *cvBufferRef)
  3460
+void CLinuxRendererGL::AddProcessor(struct __CVBuffer *cvBufferRef, int index)
3456 3461
 {
3457  
-  YUVBUFFER &buf = m_buffers[NextYV12Texture()];
  3462
+  YUVBUFFER &buf = m_buffers[index];
3458 3463
   if (buf.cvBufferRef)
3459 3464
     CVBufferRelease(buf.cvBufferRef);
3460 3465
   buf.cvBufferRef = cvBufferRef;
13  xbmc/cores/VideoRenderers/LinuxRendererGL.h
@@ -44,8 +44,6 @@ namespace Shaders { class BaseYUV2RGBShader; }
44 44
 namespace Shaders { class BaseVideoFilterShader; }
45 45
 namespace VAAPI   { struct CHolder; }
46 46
 
47  
-#define NUM_BUFFERS 3
48  
-
49 47
 
50 48
 #undef ALIGN
51 49
 #define ALIGN(value, alignment) (((value)+((alignment)-1))&~((alignment)-1))
@@ -138,15 +136,19 @@ class CLinuxRendererGL : public CBaseRenderer
138 136
   virtual void         UnInit();
139 137
   virtual void         Reset(); /* resets renderer after seek for example */
140 138
   virtual void         Flush();
  139
+  virtual void         ReleaseBuffer(int idx);
  140
+  virtual void         SetBufferSize(int numBuffers) { m_NumYV12Buffers = numBuffers; }
  141
+  virtual unsigned int GetMaxBufferSize() { return NUM_BUFFERS; }
  142
+  virtual unsigned int GetProcessorSize() { return m_NumYV12Buffers; }
141 143
 
142 144
 #ifdef HAVE_LIBVDPAU
143  
-  virtual void         AddProcessor(CVDPAU* vdpau);
  145
+  virtual void         AddProcessor(CVDPAU* vdpau, int index);
144 146
 #endif
145 147
 #ifdef HAVE_LIBVA
146  
-  virtual void         AddProcessor(VAAPI::CHolder& holder);
  148
+  virtual void         AddProcessor(VAAPI::CHolder& holder, int index);
147 149
 #endif
148 150
 #ifdef TARGET_DARWIN
149  
-  virtual void         AddProcessor(struct __CVBuffer *cvBufferRef);
  151
+  virtual void         AddProcessor(struct __CVBuffer *cvBufferRef, int index);
150 152
 #endif
151 153
 
152 154
   virtual void RenderUpdate(bool clear, DWORD flags = 0, DWORD alpha = 255);
@@ -168,7 +170,6 @@ class CLinuxRendererGL : public CBaseRenderer
168 170
   void         DrawBlackBars();
169 171
 
170 172
   bool ValidateRenderer();
171  
-  virtual void ManageTextures();
172 173
   int  NextYV12Texture();
173 174
   virtual bool ValidateRenderTarget();
174 175
   virtual void LoadShaders(int field=FIELD_FULL);
18  xbmc/cores/VideoRenderers/LinuxRendererGLES.cpp
@@ -144,13 +144,6 @@ CLinuxRendererGLES::~CLinuxRendererGLES()
144 144
   delete m_dllSwScale;
145 145
 }
146 146
 
147  
-void CLinuxRendererGLES::ManageTextures()
148  
-{
149  
-  m_NumYV12Buffers = 2;
150  
-  //m_iYV12RenderBuffer = 0;
151  
-  return;
152  
-}
153  
-
154 147
 bool CLinuxRendererGLES::ValidateRenderTarget()
155 148
 {
156 149
   if (!m_bValidated)
@@ -404,7 +397,6 @@ void CLinuxRendererGLES::Update(bool bPauseDrawing)
404 397
 {
405 398
   if (!m_bConfigured) return;
406 399
   ManageDisplay();
407  
-  ManageTextures();
408 400
 }
409 401
 
410 402
 void CLinuxRendererGLES::RenderUpdate(bool clear, DWORD flags, DWORD alpha)
@@ -418,7 +410,6 @@ void CLinuxRendererGLES::RenderUpdate(bool clear, DWORD flags, DWORD alpha)
418 410
   if (m_renderMethod & RENDER_BYPASS)
419 411
   {
420 412
     ManageDisplay();
421  
-    ManageTextures();
422 413
     // if running bypass, then the player might need the src/dst rects
423 414
     // for sizing video playback on a layer other than the gles layer.
424 415
     if (m_RenderUpdateCallBackFn)
@@ -458,7 +449,6 @@ void CLinuxRendererGLES::RenderUpdate(bool clear, DWORD flags, DWORD alpha)
458 449
     return;
459 450
 
460 451
   ManageDisplay();
461  
-  ManageTextures();
462 452
 
463 453
   g_graphicsContext.BeginPaint();
464 454
 
@@ -1991,16 +1981,16 @@ EINTERLACEMETHOD CLinuxRendererGLES::AutoInterlaceMethod()
1991 1981
 }
1992 1982
 
1993 1983
 #ifdef HAVE_LIBOPENMAX
1994  
-void CLinuxRendererGLES::AddProcessor(COpenMax* openMax, DVDVideoPicture *picture)
  1984
+void CLinuxRendererGLES::AddProcessor(COpenMax* openMax, DVDVideoPicture *picture, int index)
1995 1985
 {
1996  
-  YUVBUFFER &buf = m_buffers[NextYV12Texture()];
  1986
+  YUVBUFFER &buf = m_buffers[index];
1997 1987
   buf.openMaxBuffer = picture->openMaxBuffer;
1998 1988
 }
1999 1989
 #endif
2000 1990
 #ifdef HAVE_VIDEOTOOLBOXDECODER
2001  
-void CLinuxRendererGLES::AddProcessor(struct __CVBuffer *cvBufferRef)
  1991
+void CLinuxRendererGLES::AddProcessor(struct __CVBuffer *cvBufferRef, int index)
2002 1992
 {
2003  
-  YUVBUFFER &buf = m_buffers[NextYV12Texture()];
  1993
+  YUVBUFFER &buf = m_buffers[index];
2004 1994
   if (buf.cvBufferRef)
2005 1995
     CVBufferRelease(buf.cvBufferRef);
2006 1996
   buf.cvBufferRef = cvBufferRef;
10  xbmc/cores/VideoRenderers/LinuxRendererGLES.h
@@ -41,8 +41,6 @@ namespace Shaders { class BaseVideoFilterShader; }
41 41
 class COpenMaxVideo;
42 42
 typedef std::vector<int>     Features;
43 43
 
44  
-#define NUM_BUFFERS 3
45  
-
46 44
 
47 45
 #undef ALIGN
48 46
 #define ALIGN(value, alignment) (((value)+((alignment)-1))&~((alignment)-1))
@@ -138,6 +136,9 @@ class CLinuxRendererGLES : public CBaseRenderer
138 136
   virtual void         UnInit();
139 137
   virtual void         Reset(); /* resets renderer after seek for example */
140 138
   virtual void         ReorderDrawPoints();
  139
+  virtual void         SetBufferSize(int numBuffers) { m_NumYV12Buffers = numBuffers; }
  140
+  virtual unsigned int GetMaxBufferSize() { return NUM_BUFFERS; }
  141
+  virtual unsigned int GetProcessorSize() { return m_NumYV12Buffers; }
141 142
 
142 143
   virtual void RenderUpdate(bool clear, DWORD flags = 0, DWORD alpha = 255);
143 144
 
@@ -153,16 +154,15 @@ class CLinuxRendererGLES : public CBaseRenderer
153 154
   virtual std::vector<ERenderFormat> SupportedFormats() { return m_formats; }
154 155
 
155 156
 #ifdef HAVE_LIBOPENMAX
156  
-  virtual void         AddProcessor(COpenMax* openMax, DVDVideoPicture *picture);
  157
+  virtual void         AddProcessor(COpenMax* openMax, DVDVideoPicture *picture, int index);
157 158
 #endif
158 159
 #ifdef HAVE_VIDEOTOOLBOXDECODER
159  
-  virtual void         AddProcessor(struct __CVBuffer *cvBufferRef);
  160
+  virtual void         AddProcessor(struct __CVBuffer *cvBufferRef, int index);
160 161
 #endif
161 162
 
162 163
 protected:
163 164
   virtual void Render(DWORD flags, int index);
164 165
 
165  
-  virtual void ManageTextures();
166 166
   int  NextYV12Texture();
167 167
   virtual bool ValidateRenderTarget();
168 168
   virtual void LoadShaders(int field=FIELD_FULL);
29  xbmc/cores/VideoRenderers/OverlayRenderer.cpp
@@ -93,33 +93,32 @@ long COverlayMainThread::Release()
93 93
 CRenderer::CRenderer()
94 94
 {
95 95
   m_render = 0;
96  
-  m_decode = (m_render + 1) % 2;
97 96
 }
98 97
 
99 98
 CRenderer::~CRenderer()
100 99
 {
101  
-  for(int i = 0; i < 2; i++)
  100
+  for(int i = 0; i < NUM_BUFFERS; i++)
102 101
     Release(m_buffers[i]);
103 102
 }
104 103
 
105  
-void CRenderer::AddOverlay(CDVDOverlay* o, double pts)
  104
+void CRenderer::AddOverlay(CDVDOverlay* o, double pts, int index)
106 105
 {
107 106
   CSingleLock lock(m_section);
108 107
 
109 108
   SElement   e;
110 109
   e.pts = pts;
111 110
   e.overlay_dvd = o->Acquire();
112  
-  m_buffers[m_decode].push_back(e);
  111
+  m_buffers[index].push_back(e);
113 112
 }
114 113
 
115  
-void CRenderer::AddOverlay(COverlay* o, double pts)
  114
+void CRenderer::AddOverlay(COverlay* o, double pts, int index)
116 115
 {
117 116
   CSingleLock lock(m_section);
118 117
 
119 118
   SElement   e;
120 119
   e.pts = pts;
121 120
   e.overlay = o->Acquire();
122  
-  m_buffers[m_decode].push_back(e);
  121
+  m_buffers[index].push_back(e);
123 122
 }
124 123
 
125 124
 void CRenderer::AddCleanup(COverlay* o)
@@ -155,20 +154,26 @@ void CRenderer::Flush()
155 154
 {
156 155
   CSingleLock lock(m_section);
157 156
 
158  
-  for(int i = 0; i < 2; i++)
  157
+  for(int i = 0; i < m_iNumBuffers; i++)
159 158
     Release(m_buffers[i]);
160 159
 
  160
+  m_render = 0;
161 161
   Release(m_cleanup);
162 162
 }
163 163
 
164  
-void CRenderer::Flip()
  164
+void CRenderer::Flip(int source)
165 165
 {
166 166
   CSingleLock lock(m_section);
  167
+  if( source >= 0 && source < m_iNumBuffers )
  168
+    m_render = source;
  169
+  else
  170
+    m_render = (m_render + 1) % m_iNumBuffers;
  171
+}
167 172
 
168  
-  m_render = m_decode;
169  
-  m_decode =(m_decode + 1) % 2;
170  
-
171  
-  Release(m_buffers[m_decode]);
  173
+void CRenderer::ReleaseBuffer(int idx)
  174
+{
  175
+  CSingleLock lock(m_section);
  176
+  Release(m_buffers[idx]);
172 177
 }
173 178
 
174 179
 void CRenderer::Render()
13  xbmc/cores/VideoRenderers/OverlayRenderer.h
@@ -23,6 +23,7 @@
23 23
 #pragma once
24 24
 
25 25
 #include "threads/CriticalSection.h"
  26
+#include "BaseRenderer.h"
26 27
 
27 28
 #include <vector>
28 29
 
@@ -92,12 +93,14 @@ namespace OVERLAY {
92 93
      CRenderer();
93 94
     ~CRenderer();
94 95
 
95  
-    void AddOverlay(CDVDOverlay* o, double pts);
96  
-    void AddOverlay(COverlay*    o, double pts);
  96
+    void AddOverlay(CDVDOverlay* o, double pts, int index);
  97
+    void AddOverlay(COverlay*    o, double pts, int index);
97 98
     void AddCleanup(COverlay*    o);
98  
-    void Flip();
  99
+    void Flip(int source);
99 100
     void Render();
100 101
     void Flush();
  102
+    void SetNumBuffers(int numBuffers) { m_iNumBuffers = numBuffers; }
  103
+    void ReleaseBuffer(int idx);
101 104
 
102 105
   protected:
103 106
 
@@ -125,8 +128,8 @@ namespace OVERLAY {
125 128
     void      Release(SElementV& list);
126 129
 
127 130
     CCriticalSection m_section;
128  
-    SElementV        m_buffers[2];
129  
-    int              m_decode;
  131
+    SElementV        m_buffers[NUM_BUFFERS];
  132
+    int              m_iNumBuffers;
130 133
     int              m_render;
131 134
 
132 135
     COverlayV        m_cleanup;
322  xbmc/cores/VideoRenderers/RenderManager.cpp
@@ -28,6 +28,7 @@
28 28
 #include "utils/MathUtils.h"
29 29
 #include "threads/SingleLock.h"
30 30
 #include "utils/log.h"
  31
+#include "utils/TimeUtils.h"
31 32
 
32 33
 #include "Application.h"
33 34
 #include "ApplicationMessenger.h"
@@ -52,7 +53,7 @@
52 53
 #include "../dvdplayer/DVDCodecs/Video/DVDVideoCodec.h"
53 54
 #include "../dvdplayer/DVDCodecs/DVDCodecUtils.h"
54 55
 
55  
-#define MAXPRESENTDELAY 0.500
  56
+#define MAXPRESENTDELAY 0.200
56 57
 
57 58
 /* at any point we want an exclusive lock on rendermanager */
58 59
 /* we must make sure we don't have a graphiccontext lock */
@@ -232,7 +233,7 @@ CStdString CXBMCRenderManager::GetVSyncState()
232 233
   return state;
233 234
 }
234 235
 
235  
-bool CXBMCRenderManager::Configure(unsigned int width, unsigned int height, unsigned int d_width, unsigned int d_height, float fps, unsigned flags, ERenderFormat format, unsigned extended_format, unsigned int orientation)
  236
+bool CXBMCRenderManager::Configure(unsigned int width, unsigned int height, unsigned int d_width, unsigned int d_height, float fps, unsigned flags, ERenderFormat format, unsigned extended_format, unsigned int orientation, bool buffering)
236 237
 {
237 238
   /* make sure any queued frame was fully presented */
238 239
   double timeout = m_presenttime + 0.1;
@@ -252,6 +253,9 @@ bool CXBMCRenderManager::Configure(unsigned int width, unsigned int height, unsi
252 253
     return false;
253 254
   }
254 255
 
  256
+  // set buffering
  257
+  m_bCodecSupportsBuffering = buffering;
  258
+
255 259
   bool result = m_pRenderer->Configure(width, height, d_width, d_height, fps, flags, format, extended_format, orientation);
256 260
   if(result)
257 261
   {
@@ -266,6 +270,8 @@ bool CXBMCRenderManager::Configure(unsigned int width, unsigned int height, unsi
266 270
     m_bReconfigured = true;
267 271
     m_presentstep = PRESENT_IDLE;
268 272
     m_presentevent.Set();
  273
+    ResetRenderBuffer();
  274
+    EnableBuffering(buffering);
269 275
   }
270 276
 
271 277
   return result;
@@ -297,9 +303,13 @@ void CXBMCRenderManager::RenderUpdate(bool clear, DWORD flags, DWORD alpha)
297 303
     if (!m_pRenderer)
298 304
       return;
299 305
 
  306
+    if (m_presentstep == PRESENT_IDLE)
  307
+      PrepareNextRender();
  308
+
300 309
     if(m_presentstep == PRESENT_FLIP)
301 310
     {
302  
-      m_overlays.Flip();
  311
+      FlipRenderBuffer();
  312
+      m_overlays.Flip(m_presentsource);
303 313
       m_pRenderer->FlipPage(m_presentsource);
304 314
       m_presentstep = PRESENT_FRAME;
305 315
       m_presentevent.Set();
@@ -343,6 +353,10 @@ unsigned int CXBMCRenderManager::PreInit()
343 353
 
344 354
   UpdateDisplayLatency();
345 355
 
  356
+  m_bUseBuffering = false;
  357
+  m_bCodecSupportsBuffering = true;
  358
+  ResetRenderBuffer();
  359
+
346 360
   return m_pRenderer->PreInit();
347 361
 }
348 362
 
@@ -371,7 +385,9 @@ bool CXBMCRenderManager::Flush()
371 385
 
372 386
     CRetakeLock<CExclusiveLock> lock(m_sharedSection);
373 387
     m_pRenderer->Flush();
  388
+    m_overlays.Flush();
374 389
     m_flushEvent.Set();
  390
+    ResetRenderBuffer();
375 391
   }
376 392
   else
377 393
   {
@@ -541,23 +557,19 @@ void CXBMCRenderManager::SetViewMode(int iViewMode)
541 557
 
542 558
 void CXBMCRenderManager::FlipPage(volatile bool& bStop, double timestamp /* = 0LL*/, int source /*= -1*/, EFIELDSYNC sync /*= FS_NONE*/)
543 559
 {
544  
-  if(timestamp - GetPresentTime() > MAXPRESENTDELAY)
545  
-    timestamp =  GetPresentTime() + MAXPRESENTDELAY;
546  
-
547  
-  /* can't flip, untill timestamp */
548  
-  if(!g_graphicsContext.IsFullScreenVideo())
549  
-    WaitPresentTime(timestamp);
550  
-
551  
-  /* make sure any queued frame was fully presented */
552  
-  double timeout = m_presenttime + 1.0;
553  
-  while(m_presentstep != PRESENT_IDLE && !bStop)
  560
+  if (!m_bUseBuffering)
554 561
   {
555  
-    if(!m_presentevent.WaitMSec(100) && GetPresentTime() > timeout && !bStop)
  562
+    /* make sure any queued frame was fully presented */
  563
+    double timeout = m_presenttime + 1.0;
  564
+    while(m_presentstep != PRESENT_IDLE && !bStop)
556 565
     {
557  
-      CLog::Log(LOGWARNING, "CRenderManager::FlipPage - timeout waiting for previous frame");
558  
-      return;
  566
+      if(!m_presentevent.WaitMSec(100) && GetPresentTime() > timeout && !bStop)
  567
+      {
  568
+        CLog::Log(LOGWARNING, "CRenderManager::FlipPage - timeout waiting for previous frame");
  569
+        return;
  570
+      }
559 571
     }
560  
-  };
  572
+  }
561 573
 
562 574
   if(bStop)
563 575
     return;
@@ -565,57 +577,66 @@ void CXBMCRenderManager::FlipPage(volatile bool& bStop, double timestamp /* = 0L
565 577
   { CRetakeLock<CExclusiveLock> lock(m_sharedSection);
566 578
     if(!m_pRenderer) return;
567 579
 
568  
-    m_presenttime  = timestamp;
569  
-    m_presentfield = sync;
570  
-    m_presentstep  = PRESENT_FLIP;
571  
-    m_presentsource = source;
  580
+    EFIELDSYNC presentfield = sync;
  581
+    EPRESENTMETHOD presentmethod;
  582
+
572 583
     EDEINTERLACEMODE deinterlacemode = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode;
573 584
     EINTERLACEMETHOD interlacemethod = AutoInterlaceMethodInternal(CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod);
574 585
 
575 586
     if (deinterlacemode == VS_DEINTERLACEMODE_OFF)
576  
-      m_presentmethod = PRESENT_METHOD_SINGLE;
  587
+      presentmethod = PRESENT_METHOD_SINGLE;
577 588
     else
578 589
     {
579  
-      if (deinterlacemode == VS_DEINTERLACEMODE_AUTO && m_presentfield == FS_NONE)
580  
-        m_presentmethod = PRESENT_METHOD_SINGLE;
  590
+      if (deinterlacemode == VS_DEINTERLACEMODE_AUTO && presentfield == FS_NONE)
  591
+        presentmethod = PRESENT_METHOD_SINGLE;
581 592
       else
582 593
       {
583 594
         bool invert = false;
584  
-        if      (interlacemethod == VS_INTERLACEMETHOD_RENDER_BLEND)            m_presentmethod = PRESENT_METHOD_BLEND;
585  
-        else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_WEAVE)            m_presentmethod = PRESENT_METHOD_WEAVE;
586  
-        else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_WEAVE_INVERTED) { m_presentmethod = PRESENT_METHOD_WEAVE ; invert = true; }
587  
-        else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_BOB)              m_presentmethod = PRESENT_METHOD_BOB;
588  
-        else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_BOB_INVERTED)   { m_presentmethod = PRESENT_METHOD_BOB; invert = true; }
589  
-        else if (interlacemethod == VS_INTERLACEMETHOD_DXVA_BOB)                m_presentmethod = PRESENT_METHOD_BOB;
590  
-        else if (interlacemethod == VS_INTERLACEMETHOD_DXVA_BEST)               m_presentmethod = PRESENT_METHOD_BOB;
591  
-        else                                                                    m_presentmethod = PRESENT_METHOD_SINGLE;
  595
+        if      (interlacemethod == VS_INTERLACEMETHOD_RENDER_BLEND)            presentmethod = PRESENT_METHOD_BLEND;
  596
+        else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_WEAVE)            presentmethod = PRESENT_METHOD_WEAVE;
  597
+        else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_WEAVE_INVERTED) { presentmethod = PRESENT_METHOD_WEAVE ; invert = true; }
  598
+        else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_BOB)              presentmethod = PRESENT_METHOD_BOB;
  599
+        else if (interlacemethod == VS_INTERLACEMETHOD_RENDER_BOB_INVERTED)   { presentmethod = PRESENT_METHOD_BOB; invert = true; }
  600
+        else if (interlacemethod == VS_INTERLACEMETHOD_DXVA_BOB)                presentmethod = PRESENT_METHOD_BOB;
  601
+        else if (interlacemethod == VS_INTERLACEMETHOD_DXVA_BEST)               presentmethod = PRESENT_METHOD_BOB;
  602
+        else                                                                    presentmethod = PRESENT_METHOD_SINGLE;
592 603
 
593 604
         /* default to odd field if we want to deinterlace and don't know better */
594  
-        if (deinterlacemode == VS_DEINTERLACEMODE_FORCE && m_presentfield == FS_NONE)
595  
-          m_presentfield = FS_TOP;
  605
+        if (deinterlacemode == VS_DEINTERLACEMODE_FORCE && presentfield == FS_NONE)
  606
+          presentfield = FS_TOP;
596 607
 
597 608
         /* invert present field */
598 609
         if(invert)
599 610
         {
600  
-          if( m_presentfield == FS_BOT )
601  
-            m_presentfield = FS_TOP;
  611
+          if( presentfield == FS_BOT )
  612
+            presentfield = FS_TOP;
602 613
           else
603  
-            m_presentfield = FS_BOT;
  614
+            presentfield = FS_BOT;
604 615
         }
605 616
       }
606 617
     }
607 618
 
  619
+    FlipFreeBuffer();
  620
+    m_renderBuffers[m_iOutputRenderBuffer].timestamp = timestamp;
  621
+    m_renderBuffers[m_iOutputRenderBuffer].presentfield = presentfield;
  622
+    m_renderBuffers[m_iOutputRenderBuffer].presentmethod = presentmethod;
  623
+    if (!m_bUseBuffering)
  624
+      PrepareNextRender();
608 625
   }
609 626
 
610 627
   g_application.NewFrame();
611  
-  /* wait untill render thread have flipped buffers */
612  
-  timeout = m_presenttime + 1.0;
613  
-  while(m_presentstep == PRESENT_FLIP && !bStop)
  628
+
  629
+  if (!m_bUseBuffering)
614 630
   {
615  
-    if(!m_presentevent.WaitMSec(100) && GetPresentTime() > timeout && !bStop)
  631
+    /* wait untill render thread have flipped buffers */
  632
+    double timeout = m_presenttime + 1.0;
  633
+    while(m_presentstep == PRESENT_FLIP && !bStop)
616 634
     {
617  
-      CLog::Log(LOGWARNING, "CRenderManager::FlipPage - timeout waiting for flip to complete");
618  
-      return;
  635
+      if(!m_presentevent.WaitMSec(100) && GetPresentTime() > timeout && !bStop)
  636
+      {
  637
+        CLog::Log(LOGWARNING, "CRenderManager::FlipPage - timeout waiting for flip to complete");
  638
+        return;
  639
+      }
619 640
     }
620 641
   }
621 642
 }
@@ -679,9 +700,13 @@ void CXBMCRenderManager::Present()
679 700
     if (!m_pRenderer)
680 701
       return;
681 702
 
  703
+    if (m_presentstep == PRESENT_IDLE)
  704
+      PrepareNextRender();
  705
+
682 706
     if(m_presentstep == PRESENT_FLIP)
683 707
     {
684  
-      m_overlays.Flip();
  708
+      FlipRenderBuffer();
  709
+      m_overlays.Flip(m_presentsource);
685 710
       m_pRenderer->FlipPage(m_presentsource);
686 711
       m_presentstep = PRESENT_FRAME;
687 712
       m_presentevent.Set();
@@ -804,14 +829,14 @@ int CXBMCRenderManager::AddVideoPicture(DVDVideoPicture& pic)
804 829
   if (!m_pRenderer)
805 830
     return -1;
806 831
 
807  
-  if(m_pRenderer->AddVideoPicture(&pic))
  832
+  int index = (m_iOutputRenderBuffer + 1) % m_iNumRenderBuffers;
  833
+
  834
+  if(m_pRenderer->AddVideoPicture(&pic, index))
808 835
     return 1;
809 836
 
810 837
   YV12Image image;
811  
-  int index = m_pRenderer->GetImage(&image);
812  
-
813  
-  if(index < 0)
814  
-    return index;
  838
+  if (m_pRenderer->GetImage(&image, index) < 0)
  839
+    return -1;
815 840
 
816 841
   if(pic.format == RENDER_FMT_YUV420P
817 842
   || pic.format == RENDER_FMT_YUV420P10
@@ -834,19 +859,19 @@ int CXBMCRenderManager::AddVideoPicture(DVDVideoPicture& pic)
834 859
   }
835 860
 #ifdef HAVE_LIBVDPAU
836 861
   else if(pic.format == RENDER_FMT_VDPAU)
837  
-    m_pRenderer->AddProcessor(pic.vdpau);
  862
+    m_pRenderer->AddProcessor(pic.vdpau, index);
838 863
 #endif
839 864
 #ifdef HAVE_LIBOPENMAX
840 865
   else if(pic.format == RENDER_FMT_OMXEGL)
841  
-    m_pRenderer->AddProcessor(pic.openMax, &pic);
  866
+    m_pRenderer->AddProcessor(pic.openMax, &pic, index);
842 867
 #endif
843 868
 #ifdef TARGET_DARWIN
844 869
   else if(pic.format == RENDER_FMT_CVBREF)
845  
-    m_pRenderer->AddProcessor(pic.cvBufferRef);
  870
+    m_pRenderer->AddProcessor(pic.cvBufferRef, index);
846 871
 #endif
847 872
 #ifdef HAVE_LIBVA
848 873
   else if(pic.format == RENDER_FMT_VAAPI)
849  
-    m_pRenderer->AddProcessor(*pic.vaapi);
  874
+    m_pRenderer->AddProcessor(*pic.vaapi, index);
850 875
 #endif
851 876
   m_pRenderer->ReleaseImage(index, false);
852 877
 
@@ -908,3 +933,194 @@ EINTERLACEMETHOD CXBMCRenderManager::AutoInterlaceMethodInternal(EINTERLACEMETHO
908 933
 
909 934
   return mInt;
910 935
 }
  936
+
  937
+int CXBMCRenderManager::WaitForBuffer(volatile bool& bStop, int timeout)
  938
+{
  939
+  CSharedLock lock(m_sharedSection);
  940
+  if (!m_pRenderer)
  941
+    return -1;
  942
+
  943
+  int bufferlevel = 1;
  944
+  XbmcThreads::EndTime endtime(timeout);
  945
+  while(!HasFreeBuffer() && !bStop)
  946
+  {
  947
+    lock.Leave();
  948
+    m_flipEvent.WaitMSec(std::min(50, timeout));
  949
+    if(endtime.IsTimePast())
  950
+    {
  951
+      if (timeout != 0 && !bStop)
  952
+        CLog::Log(LOGWARNING, "CRenderManager::WaitForBuffer - timeout waiting for buffer");
  953
+      return -1;
  954
+    }
  955
+    lock.Enter();