Fixed #13574 as well as several other bugs caused by improper DVDPlayer usage in...
authorGeorge Yunaev <gyunaev@xbmc>
Sun, 23 Dec 2012 10:09:41 +0000 (02:09 -0800)
committerGeorge Yunaev <gyunaev@xbmc>
Sun, 23 Dec 2012 10:09:41 +0000 (02:09 -0800)
Tried my best to add the files into the build systems, if missed, please add xbmc/music/karaoke/karaokevideobackground.cpp and xbmc/video/FFmpegVideoDecoder.cpp into the relevant builds.

Really sorry for the delay, this should have been fixed a while ago.

12 files changed:
project/VS2010Express/XBMC.vcxproj
project/VS2010Express/XBMC.vcxproj.filters
xbmc/music/karaoke/GUIWindowKaraokeLyrics.cpp
xbmc/music/karaoke/GUIWindowKaraokeLyrics.h
xbmc/music/karaoke/Makefile.in
xbmc/music/karaoke/karaokevideobackground.cpp [new file with mode: 0644]
xbmc/music/karaoke/karaokevideobackground.h [new file with mode: 0644]
xbmc/music/karaoke/karaokewindowbackground.cpp
xbmc/music/karaoke/karaokewindowbackground.h
xbmc/video/FFmpegVideoDecoder.cpp [new file with mode: 0644]
xbmc/video/FFmpegVideoDecoder.h [new file with mode: 0644]
xbmc/video/Makefile

index fd92c6f..fb0947e 100644 (file)
     <ClCompile Include="..\..\xbmc\music\karaoke\karaokelyricstextlrc.cpp" />
     <ClCompile Include="..\..\xbmc\music\karaoke\karaokelyricstextustar.cpp" />
     <ClCompile Include="..\..\xbmc\music\karaoke\karaokewindowbackground.cpp" />
+    <ClCompile Include="..\..\xbmc\music\karaoke\karaokevideobackground.cpp" />
     <ClCompile Include="..\..\xbmc\music\LastFmManager.cpp" />
     <ClCompile Include="..\..\xbmc\music\MusicDatabase.cpp" />
     <ClCompile Include="..\..\xbmc\music\MusicDbUrl.cpp" />
     <ClInclude Include="..\..\xbmc\interfaces\python\preamble.h" />
     <ClInclude Include="..\..\xbmc\interfaces\python\PyContext.h" />
     <ClInclude Include="..\..\xbmc\interfaces\python\pythreadstate.h" />
+    <ClInclude Include="..\..\xbmc\music\karaoke\karaokevideobackground.h" />
+    <ClInclude Include="..\..\xbmc\video\FFmpegVideoDecoder.h" />
     <ClInclude Include="..\..\xbmc\interfaces\python\swig.h" />
     <ClInclude Include="..\..\xbmc\interfaces\python\XBPython.h" />
     <ClInclude Include="..\..\xbmc\interfaces\python\XBPyThread.h" />
     <ClCompile Include="..\..\xbmc\video\dialogs\GUIDialogVideoOSD.cpp" />
     <ClCompile Include="..\..\xbmc\video\dialogs\GUIDialogVideoOverlay.cpp" />
     <ClCompile Include="..\..\xbmc\video\dialogs\GUIDialogVideoSettings.cpp" />
+    <ClCompile Include="..\..\xbmc\video\FFmpegVideoDecoder.cpp" />
     <ClCompile Include="..\..\xbmc\video\GUIViewStateVideo.cpp" />
     <ClCompile Include="..\..\xbmc\video\Teletext.cpp" />
     <ClCompile Include="..\..\xbmc\video\VideoDatabase.cpp" />
     </VisualStudio>
   </ProjectExtensions>
   <Import Project="$(SolutionDir)\$(ProjectFileName).targets.user" Condition="Exists('$(SolutionDir)\$(ProjectFileName).targets.user')" />
-</Project>
\ No newline at end of file
+</Project>
index 08159c6..53a6c97 100644 (file)
     <ClCompile Include="..\..\xbmc\utils\DatabaseUtils.cpp">
       <Filter>utils</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\xbmc\music\karaoke\karaokevideobackground.cpp">
+      <Filter>music\karaoke</Filter>
+    </ClCompile>
     <ClCompile Include="..\..\xbmc\filesystem\VideoDatabaseDirectory\DirectoryNodeTags.cpp">
       <Filter>filesystem\VideoDatabaseDirectory</Filter>
     </ClCompile>
       <Filter>interfaces\swig</Filter>
     </None>
   </ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
index f3b641d..a6f67e3 100644 (file)
@@ -110,6 +110,10 @@ bool CGUIWindowKaraokeLyrics::OnMessage(CGUIMessage& message)
   return CGUIWindow::OnMessage(message);
 }
 
+void CGUIWindowKaraokeLyrics::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
+{
+  dirtyregions.push_back(CRect(0.0f, 0.0f, (float)g_graphicsContext.GetWidth(), (float)g_graphicsContext.GetHeight()));
+}
 
 void CGUIWindowKaraokeLyrics::Render()
 {
@@ -141,7 +145,7 @@ void CGUIWindowKaraokeLyrics::newSong(CKaraokeLyrics * lyrics)
 
     // Start the required video
     m_Lyrics->GetVideoParameters( path, offset );
-    m_Background->StartVideo( path, offset );
+    m_Background->StartVideo( path );
   }
   else if ( m_Lyrics->HasBackground() && g_advancedSettings.m_karaokeAlwaysEmptyOnCdgs )
   {
index 330b447..3db162d 100644 (file)
@@ -34,6 +34,7 @@ public:
   virtual bool OnMessage(CGUIMessage& message);
   virtual bool OnAction(const CAction &action);
   virtual void Render();
+  virtual void Process(unsigned int currentTime, CDirtyRegionList &dirtyregions);
 
   void    newSong( CKaraokeLyrics * lyrics );
   void    pauseSong( bool now_paused );
index a97a9a1..579ffe0 100644 (file)
@@ -8,6 +8,7 @@ SRCS += karaokelyricstext.cpp
 SRCS += karaokelyricstextkar.cpp
 SRCS += karaokelyricstextlrc.cpp
 SRCS += karaokelyricstextustar.cpp
+SRCS += karaokevideobackground.cpp
 SRCS += karaokewindowbackground.cpp
 
 LIB = karaoke.a
diff --git a/xbmc/music/karaoke/karaokevideobackground.cpp b/xbmc/music/karaoke/karaokevideobackground.cpp
new file mode 100644 (file)
index 0000000..8a3df5a
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "karaokevideobackground.h"
+
+#include "guilib/Texture.h"
+#include "guilib/GUITexture.h"
+#include "settings/Settings.h"
+#include "Application.h"
+#include "DllAvFormat.h"
+#include "DllAvCodec.h"
+#include "DllAvUtil.h"
+#include "DllSwScale.h"
+#include "filesystem/SpecialProtocol.h"
+#include "settings/AdvancedSettings.h"
+#include "video/FFmpegVideoDecoder.h"
+#include "system.h"
+
+KaraokeVideoBackground::KaraokeVideoBackground()
+{
+  m_decoder = new FFmpegVideoDecoder();
+  m_timeFromPrevSong = 0.0;
+  m_texture = 0;
+}
+
+KaraokeVideoBackground::~KaraokeVideoBackground()
+{
+  delete m_decoder;
+  delete m_texture;
+}
+
+bool KaraokeVideoBackground::openVideoFile( const CStdString& filename )
+{
+  CStdString realPath = CSpecialProtocol::TranslatePath( filename );
+
+  if ( !m_decoder->open( realPath ) )
+  {
+       CLog::Log( LOGERROR, "Karaoke Video Background: %s, video file %s (%s)", m_decoder->getErrorMsg().c_str(), filename.c_str(), realPath.c_str() );
+    return false;
+  }
+
+  m_videoWidth = m_decoder->getWidth();
+  m_videoHeight = m_decoder->getHeight();
+  m_curVideoFile = filename;
+  
+  // Find out the necessary aspect ratio for height (assuming fit by width) and width (assuming fit by height)
+  RESOLUTION res = g_graphicsContext.GetVideoResolution();
+  m_displayLeft = g_settings.m_ResInfo[res].Overscan.left;
+  m_displayRight = g_settings.m_ResInfo[res].Overscan.right;
+  m_displayTop = g_settings.m_ResInfo[res].Overscan.top;
+  m_displayBottom = g_settings.m_ResInfo[res].Overscan.bottom;
+  
+  int screen_width = m_displayRight - m_displayLeft;
+  int screen_height = m_displayBottom - m_displayTop;
+
+  // Do we need to modify the output video size? This could happen in two cases:
+  // 1. Either video dimension is larger than the screen - video needs to be downscaled
+  // 2. Both video dimensions are smaller than the screen - video needs to be upscaled
+  if ( (m_videoWidth > screen_width || m_videoHeight > screen_height )
+  || (  m_videoWidth < screen_width && m_videoHeight < screen_height ) )
+  {
+    // Calculate the scale coefficients for width/height separately
+    double scale_width = (double) screen_width / (double) m_videoWidth;
+    double scale_height = (double) screen_height / (double) m_videoHeight;
+
+    // And apply the smallest
+    double scale = scale_width < scale_height ? scale_width : scale_height;
+    m_videoWidth = (int) (m_videoWidth * scale);
+    m_videoHeight = (int) (m_videoHeight * scale);
+  }
+
+  // Calculate the desktop dimensions to show the video
+  if ( m_videoWidth < screen_width || m_videoHeight < screen_height )
+  {
+    m_displayLeft = (screen_width - m_videoWidth) / 2;
+    m_displayRight -= m_displayLeft;
+
+    m_displayTop = (screen_height - m_videoHeight) / 2;
+    m_displayBottom -= m_displayTop;
+  }
+  
+  m_millisecondsPerFrame = 1.0 / m_decoder->getFramesPerSecond();
+
+  CLog::Log( LOGDEBUG, "Karaoke Video Background: Video file %s (%dx%d) length %g seconds opened successfully, will be shown as %dx%d at (%d, %d - %d, %d) rectangle",
+             filename.c_str(), 
+                        m_decoder->getWidth(), m_decoder->getHeight(),
+                        m_decoder->getDuration(),
+                        m_videoWidth, m_videoHeight,
+             m_displayLeft, m_displayTop, m_displayRight, m_displayBottom );
+  
+  return true;
+}
+
+void KaraokeVideoBackground::closeVideoFile()
+{
+  m_decoder->close();
+}
+
+void KaraokeVideoBackground::Render()
+{
+  // Just in case
+  if ( !m_texture )
+    return;
+  
+  // Get the current song timing in ms.
+  // This will only fit songs up to 71,000 hours, so if you got a larger one, change to int64
+  double current = g_application.GetTime();
+
+  // We're supposed to show m_decoder->getFramesPerSecond() frames in one second.
+  if ( current >= m_nextFrameTime )
+  {
+       // We don't care to adjust for the exact timing as we don't worry about the exact frame rate
+       m_nextFrameTime = current + m_millisecondsPerFrame - (current - m_nextFrameTime);
+
+       while ( true )
+       {
+         if ( !m_decoder->nextFrame( m_texture ) )
+         {
+               // End of video; restart
+               m_decoder->seek( 0.0 );
+               m_nextFrameTime = 0.0;
+               continue;
+         }
+
+         break;
+       }
+  }
+
+  // We got a frame. Draw it.
+  CRect vertCoords((float) m_displayLeft, (float) m_displayTop, (float) m_displayRight, (float) m_displayBottom );
+  CGUITexture::DrawQuad(vertCoords, 0xffffffff, m_texture );
+}
+
+bool KaraokeVideoBackground::Start( const CStdString& filename )
+{
+  if ( !filename.empty() )
+  {
+     if ( !openVideoFile( filename ) )
+       return false;
+
+        m_timeFromPrevSong = 0;
+  }
+  else
+  {
+     if ( !openVideoFile( g_advancedSettings.m_karaokeDefaultBackgroundFilePath ) )
+       return false;
+
+        if ( m_timeFromPrevSong != 0.0 && !m_decoder->seek( m_timeFromPrevSong ) )
+          m_timeFromPrevSong = 0;
+  }
+  
+  // Allocate the texture
+  m_texture = new CTexture( m_videoWidth, m_videoHeight, XB_FMT_A8R8G8B8 );
+  
+  if ( !m_texture )
+  {
+    CLog::Log( LOGERROR, "Karaoke Video Background: Could not allocate texture" );
+    return false;
+  }
+  
+  m_nextFrameTime = 0.0;
+  return true;
+}
+
+void KaraokeVideoBackground::Stop()
+{
+  delete m_texture;
+  m_texture = 0;
+
+  m_timeFromPrevSong = m_decoder->getLastFrameTime();
+}
diff --git a/xbmc/music/karaoke/karaokevideobackground.h b/xbmc/music/karaoke/karaokevideobackground.h
new file mode 100644 (file)
index 0000000..705329a
--- /dev/null
@@ -0,0 +1,86 @@
+#ifndef KARAOKEVIDEOFFMPEG_H
+#define KARAOKEVIDEOFFMPEG_H
+
+/*
+ *      Copyright (C) 2005-2010 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "utils/StdString.h"
+
+class CBaseTexture;
+class FFmpegVideoDecoder;
+
+// C++ Interface: karaokevideoffmpeg
+// Contact: oldnemesis
+//
+// FFMpeg-based background video decoder for Karaoke background.
+// We are not using DVDPlayer for this because:
+// 1. DVDPlayer was not designed to run at the same time when music is being played and other things (like lyrics) rendered. 
+// While this setup works from time to time, it constantly gets broken. Some modes, like VDPAU, lead to crash right away.
+//
+// 2. We do not need to decode audio, hence we don't have to use extra CPU.
+//
+// 3. We do not really care about frame rate. Jerky video is fine for the background. Lyrics sync is much more important.
+//
+class KaraokeVideoBackground
+{
+public:
+  KaraokeVideoBackground();
+  ~KaraokeVideoBackground();
+  
+  // Start playing the video. It is called each time a new song is being played. Should continue playing existing 
+  // video from the position it was paused. If it returns false, the video rendering is disabled and 
+  // KaraokeVideoFFMpeg object is deleted. Must write the reason for failure into the log file.
+  bool Start( const CStdString& filename = "" );
+
+  // Render the current frame into the screen. This function also must handle video loops and 
+  // switching to the next video when necessary. Hence it shouldn't take too long.
+  void Render();
+
+  // Stops playing the video. It is called once the song is finished and the Render() is not going to be called anymore.
+  // The object, however, is kept and should keep its state because it must continue on next Start() call.
+  void Stop();
+  
+private:
+  // Initialize the object. This function is called only once when the object is created or after it has been dismissed. 
+  // If it returns false, the video rendering is disabled and KaraokeVideoFFMpeg object is deleted
+  bool Init();
+
+  // Dismisses the object, freeing all the memory and unloading the libraries. The object must be inited before using again.
+  void Dismiss();
+
+  bool openVideoFile( const CStdString& filename );
+  void closeVideoFile();
+  
+  // FFMpeg objects
+  FFmpegVideoDecoder * m_decoder;
+
+  CStdString       m_curVideoFile;
+  int              m_videoWidth;   // shown video width, i.e. upscaled or downscaled as necessary
+  int              m_videoHeight;  // shown video height, i.e. upscaled or downscaled as necessary
+  int              m_displayLeft, m_displayRight, m_displayTop, m_displayBottom;  // Video as shown at the display
+  double           m_millisecondsPerFrame;
+  double           m_nextFrameTime;
+  double           m_timeFromPrevSong;
+  
+  CBaseTexture    *m_texture;
+};
+
+#endif
index f6c29f0..719d463 100644 (file)
@@ -13,8 +13,9 @@
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with XBMC; see the file COPYING.  If not, see
- *  <http://www.gnu.org/licenses/>.
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
  *
  */
 
 #include "GUIUserMessages.h"
 #include "guilib/GUIVisualisationControl.h"
 #include "guilib/GUIImage.h"
-#include "cores/dvdplayer/DVDPlayer.h"
 #include "threads/SingleLock.h"
 #include "utils/log.h"
 
 #include "karaokewindowbackground.h"
+#include "karaokevideobackground.h"
 
 
 #define CONTROL_ID_VIS           1
@@ -49,19 +50,13 @@ CKaraokeWindowBackground::CKaraokeWindowBackground()
 
   m_videoPlayer = 0;
   m_parentWindow = 0;
-  m_videoLastTime = 0;
-  m_playingDefaultVideo = false;
-  m_videoEnded = false;
 }
 
 
 CKaraokeWindowBackground::~CKaraokeWindowBackground()
 {
   if ( m_videoPlayer )
-  {
-     m_videoPlayer->CloseFile();
-     delete m_videoPlayer;
-  }
+    delete m_videoPlayer;
 }
 
 
@@ -94,7 +89,6 @@ void CKaraokeWindowBackground::Init(CGUIWindow * wnd)
   {
     CLog::Log( LOGDEBUG, "Karaoke default background is video %s", g_advancedSettings.m_karaokeDefaultBackgroundFilePath.c_str() );
     m_defaultMode = BACKGROUND_VIDEO;
-    m_path = g_advancedSettings.m_karaokeDefaultBackgroundFilePath;
   }
 }
 
@@ -140,31 +134,12 @@ bool CKaraokeWindowBackground::OnMessage(CGUIMessage & message)
 
 void CKaraokeWindowBackground::Render()
 {
-  // We cannot use CSingleLock on m_CritSectionVideoEnded, because OnPlayBackEnded() might be
-  // called from a different thread, and since we cannot proceed on m_CritSectionShared, we'll deadlock
-  bool ended;
-  {
-    CSingleLock lock( m_CritSectionVideoEnded );
-    ended = m_videoEnded;
-  }
-
   CSingleLock lock (m_CritSectionShared);
-  // Do we need to restart video?
-  if ( ended )
-  {
-    NextVideo();
-    return; // VERY important! Player thread is locked now, and if we continue, we'll lock in g_renderManager.Present()
-  }
 
   // Proceed with video rendering
-  if ( m_currentMode == BACKGROUND_VIDEO )
+  if ( m_currentMode == BACKGROUND_VIDEO && m_videoPlayer )
   {
-#ifdef HAS_VIDEO_PLAYBACK
-    if ( g_application.IsPresentFrame() )
-      g_renderManager.Present();
-    else
-      g_renderManager.RenderUpdate(true, 0, 255);
-#endif
+    m_videoPlayer->Render();
   }
 
   // For other visualisations just disable the screen saver
@@ -205,44 +180,19 @@ void CKaraokeWindowBackground::StartImage( const CStdString& path )
 }
 
 
-void CKaraokeWindowBackground::StartVideo( const CStdString& path, int64_t offset)
+void CKaraokeWindowBackground::StartVideo( const CStdString& path )
 {
-  CFileItem item( path, false);
-  m_videoEnded = false;
-
-  // Video options
-  CPlayerOptions options;
-  options.video_only = true;
-
-  if ( offset > 0 )
-    options.starttime = (double) (offset / 1000.0);
-
-  if ( !item.IsVideo() )
-  {
-    CLog::Log(LOGERROR, "KaraokeVideoBackground: file %s is not a video file, ignoring", path.c_str() );
-    return;
-  }
-
-  if ( item.IsDVD() )
-  {
-    CLog::Log(LOGERROR, "KaraokeVideoBackground: DVD video playback is not supported");
-    return;
-  }
-
   if ( !m_videoPlayer )
-    m_videoPlayer = new CDVDPlayer(*this);
+       m_videoPlayer = new KaraokeVideoBackground();
 
-  if ( !m_videoPlayer )
-    return;
-
-  if ( !m_videoPlayer->OpenFile( item, options ) )
+  if ( !m_videoPlayer->Start( path ) )
   {
-    CLog::Log(LOGERROR, "KaraokeVideoBackground: error opening video file %s", item.GetPath().c_str());
+    delete m_videoPlayer;
+    m_videoPlayer = 0;
+    m_currentMode = BACKGROUND_NONE;
     return;
   }
-
-  CLog::Log(LOGDEBUG, "KaraokeVideoBackground: video file %s opened successfully", item.GetPath().c_str());
-
+  
   m_ImgControl->SetVisible( false );
   m_VisControl->SetVisible( false );
   m_currentMode = BACKGROUND_VIDEO;
@@ -266,10 +216,7 @@ void CKaraokeWindowBackground::StartDefault()
       break;
 
     case BACKGROUND_VIDEO:
-      StartVideo( m_path, m_videoLastTime );
-
-      if ( m_currentMode == BACKGROUND_VIDEO )
-        m_playingDefaultVideo = true;
+      StartVideo();
       break;
 
     default:
@@ -284,16 +231,8 @@ void CKaraokeWindowBackground::Stop()
   CSingleLock lock (m_CritSectionShared);
   m_currentMode = BACKGROUND_NONE;
 
-  // Disable and hide all control
   if ( m_videoPlayer )
-  {
-     if ( m_playingDefaultVideo )
-       m_videoLastTime = m_videoPlayer->GetTime();
-
-     m_videoPlayer->CloseFile();
-     delete m_videoPlayer;
-     m_videoPlayer = 0;
-  }
+    m_videoPlayer->Stop();
 
   CLog::Log( LOGDEBUG, "Karaoke background stopped" );
 }
@@ -301,57 +240,20 @@ void CKaraokeWindowBackground::Stop()
 
 void CKaraokeWindowBackground::OnPlayBackEnded()
 {
-  CSingleLock lock ( m_CritSectionVideoEnded );
-  m_videoEnded = true;
-/*  CSingleLock lock (m_CritSectionShared);
-
-  CLog::Log( LOGDEBUG, "KaraokeVideoBackground: stopping" );
-  Stop();
-  CLog::Log( LOGDEBUG, "KaraokeVideoBackground: stopped" );
-  m_videoLastTime = 0;
-  StartVideo( m_path, m_videoLastTime );
-  CLog::Log( LOGDEBUG, "KaraokeVideoBackground: restarting video from the beginning" );
-*/
 }
 
-
 void CKaraokeWindowBackground::OnPlayBackStarted()
 {
 }
 
-
 void CKaraokeWindowBackground::OnPlayBackStopped()
 {
 }
 
-
 void CKaraokeWindowBackground::OnQueueNextItem()
 {
 }
 
 void CKaraokeWindowBackground::Pause(bool now_paused)
 {
-  if ( m_currentMode == BACKGROUND_VIDEO && m_videoPlayer )
-  {
-    if ( (now_paused && !m_videoPlayer->IsPaused())
-    || ( !now_paused && m_videoPlayer->IsPaused() ) )
-      m_videoPlayer->Pause();
-  }
-}
-
-void CKaraokeWindowBackground::NextVideo()
-{
-  // This function should not be called directly from the callback! Deadlock!!!
-  m_videoPlayer->CloseFile();
-
-  // Only one video selected, restarting
-  m_videoLastTime = 0;
-
-  {
-    CSingleLock lock(m_CritSectionVideoEnded );
-    m_videoEnded = false;
-  }
-
-  StartVideo( m_path, m_videoLastTime );
-  CLog::Log( LOGDEBUG, "KaraokeVideoBackground: restarting video from the beginning" );
 }
index 3e6ee11..0cdc909 100644 (file)
@@ -28,7 +28,7 @@
 class CGUIWindow;
 class CGUIImage;
 class CGUIVisualisationControl;
-class CDVDPlayer;
+class KaraokeVideoBackground;
 
 class CKaraokeWindowBackground : public IPlayerCallback
 {
@@ -48,7 +48,7 @@ public:
   virtual void StartImage( const CStdString& path );
 
   // Start with song-specific video background
-  virtual void StartVideo( const CStdString& path, int64_t offset = 0 );
+  virtual void StartVideo( const CStdString& path = "" );
 
   // Start with default (setting-specific) background
   virtual void StartDefault();
@@ -59,9 +59,6 @@ public:
   // Stop any kind of background
   virtual void Stop();
 
-  // Switch to next video, or restart current one
-  virtual void NextVideo();
-
   // Function forwarders
   virtual bool OnAction(const CAction &action);
   virtual bool OnMessage(CGUIMessage& message);
@@ -85,10 +82,6 @@ private:
   // This critical section protects all variables except m_videoEnded
   CCriticalSection          m_CritSectionShared;
 
-  // This critical section protects m_videoEnded, since it could be changed from a different thread
-  // while the section above is locked
-  CCriticalSection          m_CritSectionVideoEnded;
-
   // for visualization background
   CGUIVisualisationControl * m_VisControl;
   CGUIImage                * m_ImgControl;
@@ -99,14 +92,11 @@ private:
   CGUIWindow               * m_parentWindow;
 
   // Video player pointer
-  CDVDPlayer               * m_videoPlayer;
-  bool                       m_videoEnded;
+  KaraokeVideoBackground   * m_videoPlayer;
 
   // For default visualisation mode
   BackgroundMode             m_defaultMode;
-  CStdString                 m_path; // image or video
-  int64_t                    m_videoLastTime; // video only
-  bool                       m_playingDefaultVideo; // whether to store the time
+  CStdString                 m_path; // image
 };
 
 #endif
diff --git a/xbmc/video/FFmpegVideoDecoder.cpp b/xbmc/video/FFmpegVideoDecoder.cpp
new file mode 100644 (file)
index 0000000..1636160
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ *      Copyright (C) 2005-2012 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "system.h"
+#include "DllAvFormat.h"
+#include "DllAvCodec.h"
+#include "DllAvUtil.h"
+#include "DllSwScale.h"
+#include "guilib/Texture.h"
+
+#include "FFmpegVideoDecoder.h"
+
+
+FFmpegVideoDecoder::FFmpegVideoDecoder()
+{
+  m_pFormatCtx = 0;
+  m_pCodecCtx = 0;
+  m_pCodec = 0;
+  m_pFrame = 0;
+  m_pFrameRGB = 0;
+  
+  m_dllAvFormat = new DllAvFormat();
+  m_dllAvCodec = new DllAvCodec();
+  m_dllAvUtil = new DllAvUtil();
+  m_dllSwScale = new DllSwScale();
+}
+
+FFmpegVideoDecoder::~FFmpegVideoDecoder()
+{
+  close();
+  
+  delete m_dllAvFormat;
+  delete m_dllAvCodec;
+  delete m_dllAvUtil;
+  delete m_dllSwScale;
+}
+
+void FFmpegVideoDecoder::close()
+{
+  // Free the YUV frame
+  if ( m_pFrame )
+       m_dllAvUtil->av_free( m_pFrame );
+
+  // Free the RGB frame
+  if ( m_pFrameRGB )
+  {
+       m_dllAvCodec->avpicture_free( m_pFrameRGB );
+       m_dllAvUtil->av_free( m_pFrameRGB );
+  }
+
+  // Close the codec
+  if ( m_pCodecCtx )
+       m_dllAvCodec->avcodec_close( m_pCodecCtx );
+
+  // Close the video file
+  if ( m_pFormatCtx )
+       m_dllAvFormat->avformat_close_input( &m_pFormatCtx );
+
+  m_pFormatCtx = 0;
+  m_pCodecCtx = 0;
+  m_pCodec = 0;
+  m_pFrame = 0;
+  m_pFrameRGB = 0;
+  
+  if ( m_dllAvCodec->IsLoaded() )
+    m_dllAvCodec->Unload();
+  
+  if ( m_dllAvUtil->IsLoaded() )
+    m_dllAvUtil->Unload();
+  
+  if ( m_dllSwScale->IsLoaded() )
+    m_dllSwScale->Unload();
+  
+  if ( m_dllAvFormat->IsLoaded() )
+    m_dllAvFormat->Unload();
+}
+
+bool FFmpegVideoDecoder::isOpened() const
+{
+  return m_pFrame ? true : false;
+}
+
+double FFmpegVideoDecoder::getDuration() const
+{
+  if ( m_pFormatCtx && m_pFormatCtx->duration / AV_TIME_BASE > 0.0 )
+       return m_pFormatCtx->duration / AV_TIME_BASE;
+
+  return 0.0;
+}
+  
+double FFmpegVideoDecoder::getFramesPerSecond() const
+{
+  return m_pFormatCtx ? av_q2d( m_pFormatCtx->streams[ m_videoStream ]->r_frame_rate ) : 0.0;
+}
+  
+unsigned int FFmpegVideoDecoder::getWidth() const
+{
+  if ( !m_pCodecCtx )
+    return 0;
+
+  return m_pCodecCtx->width;
+}
+
+unsigned int FFmpegVideoDecoder::getHeight() const
+{
+  if ( !m_pCodecCtx )
+    return 0;
+
+  return m_pCodecCtx->height;
+}
+
+const AVFormatContext * FFmpegVideoDecoder::getAVFormatContext() const
+{
+  return m_pFormatCtx;
+}
+
+const AVCodecContext * FFmpegVideoDecoder::getAVCodecContext() const
+{
+  return m_pCodecCtx;
+}
+
+const AVCodec * FFmpegVideoDecoder::getAVCodec() const
+{
+  return m_pCodec;
+}
+
+CStdString FFmpegVideoDecoder::getErrorMsg() const
+{
+  return m_errorMsg;
+}
+
+double FFmpegVideoDecoder::getLastFrameTime() const
+{
+  return m_lastFrameTime;
+}
+
+
+bool FFmpegVideoDecoder::open( const CStdString& filename )
+{
+  // See http://dranger.com/ffmpeg/tutorial01.html
+  close();
+  
+  if ( !m_dllAvUtil->Load() || !m_dllAvCodec->Load() || !m_dllSwScale->Load() || !m_dllAvFormat->Load() )
+  {
+    m_errorMsg = "Failed to load FFMpeg libraries";
+    return false;
+  }
+
+  m_dllAvCodec->avcodec_register_all();
+  m_dllAvFormat->av_register_all();
+
+  // Open the video file
+  if ( m_dllAvFormat->avformat_open_input( &m_pFormatCtx, filename.c_str(), NULL, NULL ) < 0 )
+  {
+    m_errorMsg = "Could not open the video file";
+   close();
+    return false;
+  }
+
+  // Retrieve the stream information
+  if ( m_dllAvFormat->avformat_find_stream_info( m_pFormatCtx, 0 ) < 0 )
+  {
+    m_errorMsg = "Could not find the stream information";
+    close();
+    return false;
+  }
+
+  // Find the first video stream
+  m_videoStream = -1;
+
+  for ( unsigned i = 0; i < m_pFormatCtx->nb_streams; i++ )
+  {
+    if ( m_pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
+    {
+      m_videoStream = i;
+      break;
+    }
+  }
+
+  if ( m_videoStream == -1 )
+  {
+    m_errorMsg = "Could not find a playable video stream";
+    close();
+    return false;
+  }
+
+  // Get a pointer to the codec context for the video stream
+  m_pCodecCtx = m_pFormatCtx->streams[ m_videoStream ]->codec;
+
+  // Find the decoder for the video stream
+  m_pCodec = m_dllAvCodec->avcodec_find_decoder( m_pCodecCtx->codec_id );
+
+  if ( m_pCodec == NULL )
+  {
+    m_errorMsg = "Could not find a video decoder";
+    close();
+    return false;
+  }
+    
+  // Open the codec
+  if ( m_dllAvCodec->avcodec_open2( m_pCodecCtx, m_pCodec, 0 ) < 0 )
+  {
+    m_errorMsg = "Could not open the video decoder";
+    close();
+    return false;
+  }
+  
+  // Allocate video frames
+  m_pFrame = m_dllAvCodec->avcodec_alloc_frame();
+
+  if ( !m_pFrame )
+  {
+    m_errorMsg = "Could not allocate memory for a frame";
+    close();
+    return false;
+  }
+  
+  return true;
+}
+
+
+bool FFmpegVideoDecoder::seek( double time )
+{
+  // Convert the frame number into time stamp
+  int64_t timestamp = (int64_t) (time * AV_TIME_BASE * av_q2d( m_pFormatCtx->streams[ m_videoStream ]->time_base ));
+
+  if ( m_dllAvFormat->av_seek_frame( m_pFormatCtx, m_videoStream, timestamp, AVSEEK_FLAG_ANY ) < 0 )
+       return false;
+
+  m_dllAvCodec->avcodec_flush_buffers( m_pCodecCtx );
+  return true;
+}
+
+bool FFmpegVideoDecoder::nextFrame( CBaseTexture * texture )
+{
+  // Just in case
+  if ( !m_pCodecCtx )
+       return false;
+
+  // If we did not preallocate the picture or the texture size changed, (re)allocate it
+  if ( !m_pFrameRGB || texture->GetWidth() != m_frameRGBwidth || texture->GetHeight() != m_frameRGBheight )
+  {
+    if ( m_pFrameRGB )
+    {
+      m_dllAvCodec->avpicture_free( m_pFrameRGB );
+      m_dllAvUtil->av_free( m_pFrameRGB );
+    }
+
+    m_frameRGBwidth = texture->GetWidth();
+    m_frameRGBheight = texture->GetHeight();
+
+    // Allocate the conversion frame and relevant picture
+    m_pFrameRGB = (AVPicture*)m_dllAvUtil->av_mallocz(sizeof(AVPicture));
+
+    if ( !m_pFrameRGB )
+      return false;
+
+    // Due to a bug in swsscale we need to allocate one extra line of data
+    if ( m_dllAvCodec->avpicture_alloc( m_pFrameRGB, PIX_FMT_RGB32, m_frameRGBwidth, m_frameRGBheight + 1 ) < 0 )
+      return false;
+  }
+
+  AVPacket packet;
+  int frameFinished;
+
+  while ( true )
+  {
+    // Read a frame
+    if ( m_dllAvFormat->av_read_frame( m_pFormatCtx, &packet ) < 0 )
+      return false;  // Frame read failed (e.g. end of stream)
+
+    if ( packet.stream_index == m_videoStream )
+    {
+      // Is this a packet from the video stream -> decode video frame
+      m_dllAvCodec->avcodec_decode_video2( m_pCodecCtx, m_pFrame, &frameFinished, &packet );
+
+      // Did we get a video frame?
+      if ( frameFinished )
+      {
+        if ( packet.dts != AV_NOPTS_VALUE )
+         m_lastFrameTime = packet.dts * av_q2d( m_pFormatCtx->streams[ m_videoStream ]->time_base );
+        else
+          m_lastFrameTime = 0.0;
+
+       break;
+      }
+    }
+
+    m_dllAvCodec->av_free_packet( &packet );
+  }
+
+  // We got the video frame, render it into the picture buffer
+  struct SwsContext * context = m_dllSwScale->sws_getContext( m_pCodecCtx->width, m_pCodecCtx->height, m_pCodecCtx->pix_fmt,
+                           m_frameRGBwidth, m_frameRGBheight, PIX_FMT_RGB32, SWS_FAST_BILINEAR, NULL, NULL, NULL );
+
+  m_dllSwScale->sws_scale( context, m_pFrame->data, m_pFrame->linesize, 0, m_pCodecCtx->height, 
+                                                                     m_pFrameRGB->data, m_pFrameRGB->linesize );
+  m_dllSwScale->sws_freeContext( context );
+  m_dllAvCodec->av_free_packet( &packet );
+
+  // And into the texture
+  texture->Update( m_frameRGBwidth, m_frameRGBheight, m_frameRGBwidth * 4, XB_FMT_A8R8G8B8, m_pFrameRGB->data[0], false );
+
+  return true;
+}
+
diff --git a/xbmc/video/FFmpegVideoDecoder.h b/xbmc/video/FFmpegVideoDecoder.h
new file mode 100644 (file)
index 0000000..bead0f1
--- /dev/null
@@ -0,0 +1,164 @@
+#ifndef FFMPEGVIDEODECODER_H
+#define FFMPEGVIDEODECODER_H
+
+/*
+ *      Copyright (C) 2005-2012 Team XBMC
+ *      http://www.xbmc.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with XBMC; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "utils/StdString.h"
+
+class CBaseTexture;
+class m_dllAvFormat;
+class m_dllAvCodec;
+class m_dllAvUtil;
+class m_dllSwScale;
+
+struct AVFormatContext;
+struct AVCodecContext;
+struct AVCodec;
+struct AVFrame;
+
+/**
+ * A simple FFMpeg-based background video decoder.
+ * 
+ * This class only decodes the video using the standard FFmpeg calls, so likely no hardware acceleration.
+ * No audio decoding, and no rendering.
+ * 
+ */
+class FFmpegVideoDecoder
+{
+public:
+  FFmpegVideoDecoder();
+  ~FFmpegVideoDecoder();
+
+  /**
+   * Opens the video file for decoding. Supports all the formats supported by the used FFmpeg.
+   * The file must have at least one video track. If it has more than one, the first video track
+   * would be decoded.
+   * 
+   * If an existing stream was opened, it is automatically closed and the new stream is opened.
+   * 
+   * Returns true if the file was opened successfully, and false otherwise, in which case
+   * the getErrorMsg() function could be used to retrieve the reason.
+   * 
+   * @param filename The video file name, which must be translated through CSpecialProtocol::TranslatePath()
+   */
+  bool open( const CStdString& filename );
+
+  /**
+   * Returns true if the decoder has the video file opened.
+   */
+  bool isOpened() const;
+
+  /**
+   * Returns the movie duration in seconds or 0.0 if the duration is not available. For some formats
+   * is calculated through the heuristics, and the video might not really be that long (for example if it is incomplete).
+   * The total number of frames is calculated by multiplying the duration by getFramesPerSecond()
+   */
+  double getDuration() const;
+  
+  /**
+   * Returns the frames per second for this video
+   */
+  double getFramesPerSecond() const;
+  
+  /**
+   * Returns the original video width or 0 if the video wasn't opened.
+   */  
+  unsigned int getWidth() const;
+
+  /**
+   * Returns the original video height or 0 if the video wasn't opened.
+   */  
+  unsigned int getHeight() const;
+
+  /**
+   * Returns the last rendered frame number.
+   */
+  double getLastFrameTime() const;
+
+  /**
+   * Returns the AVFormatContext structure associated with this video format
+   */  
+  const AVFormatContext * getAVFormatContext() const;
+
+  /**
+   * Returns the AVCodecContext structure associated with this video codec
+   */  
+  const AVCodecContext * getAVCodecContext() const;
+  
+  /**
+   * Returns the AVCodec structure associated with this video codec
+   */  
+  const AVCodec * getAVCodec() const;
+
+  /**
+   * Returns the error message text if opening the video failed
+   */  
+  CStdString getErrorMsg() const;
+
+  /**
+   * Decodes and renders the next video frame into the provided texture which
+   * must be in a XB_FMT_A8R8G8B8 format.
+   * The frame will be rescaled to fit the whole texture (i.e. texture width/height)
+   * so make sure the texture aspect ratio is the same as in the original movie.
+   * 
+   * @param texture The texture to render the frame into. Must be preallocated, 
+   * have the appropriate width/height and in XB_FMT_A8R8G8B8 format.
+   * 
+   * @return true if the frame rendered, false if there are no more frames
+   */  
+  bool nextFrame( CBaseTexture * texture );
+
+  /**
+       * Seeks to a specific time position in the video file. Note that the seek is limited to the keyframes only.
+       * @param time The time to seek to, in seconds
+       * @return true if the seek succeed, false if failed
+       */
+  bool seek( double time );
+
+  /**
+   * Closes the video stream.
+   */  
+  void close();
+
+private:
+  bool readFrame( int frame );
+  
+  DllAvFormat     *m_dllAvFormat;
+  DllAvCodec      *m_dllAvCodec;
+  DllAvUtil       *m_dllAvUtil;
+  DllSwScale      *m_dllSwScale;
+  AVFormatContext *m_pFormatCtx;
+  AVCodecContext  *m_pCodecCtx;
+  AVCodec         *m_pCodec;
+  AVFrame         *m_pFrame;
+  AVPicture       *m_pFrameRGB;
+  int              m_videoStream;
+  double           m_lastFrameTime;
+  
+  // The dimensions of the allocated pFrameRGB
+  unsigned int     m_frameRGBwidth;
+  unsigned int     m_frameRGBheight;
+  
+  CStdString       m_errorMsg;
+};
+
+#endif
index 1ebcf99..56449c0 100644 (file)
@@ -1,4 +1,5 @@
 SRCS=Bookmark.cpp \
+     FFmpegVideoDecoder.cpp \
      GUIViewStateVideo.cpp \
      Teletext.cpp \
      VideoDatabase.cpp \