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.
<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>
<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>
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()
{
// 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 )
{
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 );
SRCS += karaokelyricstextkar.cpp
SRCS += karaokelyricstextlrc.cpp
SRCS += karaokelyricstextustar.cpp
+SRCS += karaokevideobackground.cpp
SRCS += karaokewindowbackground.cpp
LIB = karaoke.a
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+#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
* 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
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;
}
{
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;
}
}
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
}
-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;
break;
case BACKGROUND_VIDEO:
- StartVideo( m_path, m_videoLastTime );
-
- if ( m_currentMode == BACKGROUND_VIDEO )
- m_playingDefaultVideo = true;
+ StartVideo();
break;
default:
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" );
}
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" );
}
class CGUIWindow;
class CGUIImage;
class CGUIVisualisationControl;
-class CDVDPlayer;
+class KaraokeVideoBackground;
class CKaraokeWindowBackground : public IPlayerCallback
{
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();
// 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);
// 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;
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
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+#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
SRCS=Bookmark.cpp \
+ FFmpegVideoDecoder.cpp \
GUIViewStateVideo.cpp \
Teletext.cpp \
VideoDatabase.cpp \