Index: project/VS2008Express/XBMC.vcproj
===================================================================
--- project/VS2008Express/XBMC.vcproj	(revision 21009)
+++ project/VS2008Express/XBMC.vcproj	(working copy)
@@ -3034,6 +3034,14 @@
 					>
 				</File>
 				<File
+					RelativePath="..\..\xbmc\utils\StreamDetails.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\xbmc\utils\StreamDetails.h"
+					>
+				</File>
+				<File
 					RelativePath="..\..\xbmc\StringUtils.cpp"
 					>
 				</File>
Index: xbmc/Application.cpp
===================================================================
--- xbmc/Application.cpp	(revision 21009)
+++ xbmc/Application.cpp	(working copy)
@@ -4421,6 +4421,23 @@
       if( options.fullscreen && g_renderManager.IsStarted()
        && m_gWindowManager.GetActiveWindow() != WINDOW_FULLSCREEN_VIDEO )
        SwitchToFullScreen();
+
+      // Save information about the stream if we currently have no data
+      if (item.HasVideoInfoTag())
+      {
+        CVideoInfoTag *details = m_itemCurrentFile->GetVideoInfoTag();
+        if (!details->HasStreamDetails())
+        {
+          if (m_pPlayer->GetStreamDetails(details->m_streamDetails) && details->HasStreamDetails())
+          {
+            CVideoDatabase dbs;
+            dbs.Open();
+            dbs.SetStreamDetailsForFileId(details->m_streamDetails, details->m_iFileId);
+            dbs.Close();
+            CUtil::DeleteVideoDatabaseDirectoryCache();
+          }
+        }
+      }
     }
 #endif
 
Index: xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h
===================================================================
--- xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h	(revision 21009)
+++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h	(working copy)
@@ -74,6 +74,7 @@
     iId = 0;
     iPhysicalId = 0;
     codec = (CodecID)0; // CODEC_ID_NONE
+    codec_fourcc = 0;
     type = STREAM_NONE;
     source = STREAM_SOURCE_NONE;
     iDuration = 0;
@@ -99,6 +100,7 @@
   int iId;         // most of the time starting from 0
   int iPhysicalId; // id
   CodecID codec;
+  unsigned int codec_fourcc; // if available
   StreamType type;
   int source;
 
@@ -294,4 +296,8 @@
    */
   CDemuxStreamSubtitle* GetStreamFromSubtitleId(int iSubtitleIndex);
 
+  /*
+   * return a user-presentable codec name of the given stream
+   */
+  virtual void GetStreamCodecName(int iStreamId, CStdString &strName) {};
 };
Index: xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
===================================================================
--- xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp	(revision 21009)
+++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp	(working copy)
@@ -69,10 +69,13 @@
 // these need to be put somewhere that are compiled, we should have some better place for it
 
 CCriticalSection DllAvCodec::m_critSection;
+std::map<DWORD, CStdString>logbuffer;
 
 void ff_avutil_log(void* ptr, int level, const char* format, va_list va)
 {
-  static CStdString buffer;
+  CSingleLock lock(DllAvCodec::m_critSection);
+  DWORD threadId = GetCurrentThreadId();
+  CStdString &buffer = logbuffer[threadId];
 
   AVClass* avc= ptr ? *(AVClass**)ptr : NULL;
 
@@ -93,7 +96,7 @@
   CStdString message, prefix;
   message.FormatV(format, va);
 
-  prefix = "ffmpeg: ";
+  prefix.Format("ffmpeg[%X]: ", threadId);
   if(avc)
   {
     if(avc->item_name)
@@ -969,6 +972,7 @@
     if (pStream->duration != (int64_t)AV_NOPTS_VALUE) m_streams[iId]->iDuration = (int)((pStream->duration / AV_TIME_BASE) & 0xFFFFFFFF);
 
     m_streams[iId]->codec = pStream->codec->codec_id;
+    m_streams[iId]->codec_fourcc = pStream->codec->codec_tag;
     m_streams[iId]->iId = iId;
     m_streams[iId]->source = STREAM_SOURCE_DEMUX;
     m_streams[iId]->pPrivate = pStream;
@@ -1105,3 +1109,34 @@
         return false;
     #endif
 }
+
+void CDVDDemuxFFmpeg::GetStreamCodecName(int iStreamId, CStdString &strName)
+{
+  CDemuxStream *stream = GetStream(iStreamId);
+  if (stream)
+  {
+    unsigned int in = stream->codec_fourcc;
+    // audio codecs have 2 byte IDs that don't make much sense so only use FourCC for video
+    if (stream->type == STREAM_VIDEO && in != 0)
+    {
+      char fourcc[5];
+#if defined(__powerpc__)
+      fourcc[0] = in & 0xff;
+      fourcc[1] = (in >> 8) & 0xff;
+      fourcc[2] = (in >> 16) & 0xff;
+      fourcc[3] = (in >> 24) & 0xff;
+#else
+      *(unsigned int*)fourcc = in;
+#endif
+      fourcc[4] = 0;
+      strName = fourcc;
+      strName.MakeLower();
+    }
+    else if (m_dllAvCodec.IsLoaded())
+    {
+      AVCodec *codec = m_dllAvCodec.avcodec_find_decoder(stream->codec);
+      if (codec)
+        strName = codec->name;
+    }
+  }
+}
Index: xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h
===================================================================
--- xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h	(revision 21009)
+++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h	(working copy)
@@ -96,8 +96,8 @@
   int GetChapterCount();
   int GetChapter();
   void GetChapterName(std::string& strChapterName);
+  virtual void GetStreamCodecName(int iStreamId, CStdString &strName);
 
-
   bool Aborted();
 
   AVFormatContext* m_pFormatContext;
Index: xbmc/cores/dvdplayer/DVDFileInfo.cpp
===================================================================
--- xbmc/cores/dvdplayer/DVDFileInfo.cpp	(revision 21009)
+++ xbmc/cores/dvdplayer/DVDFileInfo.cpp	(working copy)
@@ -23,8 +23,10 @@
 #include "FileItem.h"
 #include "Settings.h"
 #include "Picture.h"
+#include "VideoInfoTag.h"
+#include "Util.h"
+#include "FileSystem/StackDirectory.h"
 
-
 #include "DVDFileInfo.h"
 #include "DVDStreamInfo.h"
 #include "DVDInputStreams/DVDInputStream.h"
@@ -66,7 +68,7 @@
     return false;
 }
 
-bool CDVDFileInfo::ExtractThumb(const CStdString &strPath, const CStdString &strTarget)
+bool CDVDFileInfo::ExtractThumb(const CStdString &strPath, const CStdString &strTarget, CStreamDetails *pStreamDetails)
 {
   int nTime = timeGetTime();
   CDVDInputStream *pInputStream = CDVDFactoryInputStream::CreateInputStream(NULL, strPath, "");
@@ -112,6 +114,9 @@
     return false;
   }
 
+  if (pStreamDetails)
+    DemuxerToStreamDetails(pDemuxer, *pStreamDetails);
+
   CDemuxStream* pStream = NULL;
   int nVideoStream = -1;
   for (int i = 0; i < pDemuxer->GetNrOfStreams(); i++)
@@ -310,3 +315,94 @@
 
 }
 
+/**
+ * \brief Open the item pointed to by pItem and extact streamdetails
+ * \return true if the stream details have changed
+ */
+bool CDVDFileInfo::GetFileStreamDetails(CFileItem *pItem)
+{
+  if (!pItem)
+    return false;
+
+  CStdString strFileNameAndPath;
+  if (pItem->HasVideoInfoTag())
+  {
+    strFileNameAndPath = pItem->GetVideoInfoTag()->m_strFileNameAndPath;
+    if (CUtil::IsStack(strFileNameAndPath))
+      strFileNameAndPath = DIRECTORY::CStackDirectory::GetFirstStackedFile(strFileNameAndPath);
+  }
+  else
+    return false;
+
+  CDVDInputStream *pInputStream = CDVDFactoryInputStream::CreateInputStream(NULL, strFileNameAndPath, "");
+  if (!pInputStream)
+    return false;
+
+  if (pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) || !pInputStream->Open(strFileNameAndPath.c_str(), ""))
+  {
+    delete pInputStream;
+    return false;
+  }
+
+  CDVDDemux *pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(pInputStream);
+  if (pDemuxer)
+  {
+    bool retVal = DemuxerToStreamDetails(pDemuxer, pItem->GetVideoInfoTag()->m_streamDetails);
+    delete pDemuxer;
+    delete pInputStream;
+    return retVal;
+  }
+  else
+  {
+    delete pInputStream;
+    return false;
+  }
+}
+
+/* returns true if details have been added */
+bool CDVDFileInfo::DemuxerToStreamDetails(CDVDDemux *pDemux, CStreamDetails &details)
+{
+  bool retVal = false;
+  details.Reset();
+
+  for (int iStream=0; iStream<pDemux->GetNrOfStreams(); iStream++)
+  {
+    CDemuxStream *stream = pDemux->GetStream(iStream);
+    if (stream->type == STREAM_VIDEO)
+    {
+      CStreamDetailVideo *p = new CStreamDetailVideo();
+      p->m_iWidth = ((CDemuxStreamVideo *)stream)->iWidth;
+      p->m_iHeight = ((CDemuxStreamVideo *)stream)->iHeight;
+      p->m_fAspect = ((CDemuxStreamVideo *)stream)->fAspect;
+      pDemux->GetStreamCodecName(iStream, p->m_strCodec);
+      details.AddStream(p);
+      retVal = true;
+    }
+
+    else if (stream->type == STREAM_AUDIO)
+    {
+      CStreamDetailAudio *p = new CStreamDetailAudio();
+      p->m_iChannels = ((CDemuxStreamAudio *)stream)->iChannels;
+      if (stream->language)
+        p->m_strLanguage = stream->language;
+      pDemux->GetStreamCodecName(iStream, p->m_strCodec);
+      details.AddStream(p);
+      retVal = true;
+    }
+
+    else if (stream->type == STREAM_SUBTITLE)
+    {
+      if (stream->language)
+      {
+        CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
+        p->m_strLanguage = stream->language;
+        details.AddStream(p);
+        retVal = true;
+      }
+    }
+  }  /* for iStream */
+
+  details.DetermineBestStreams();
+  return retVal;
+}
+
Index: xbmc/cores/dvdplayer/DVDFileInfo.h
===================================================================
--- xbmc/cores/dvdplayer/DVDFileInfo.h	(revision 21009)
+++ xbmc/cores/dvdplayer/DVDFileInfo.h	(working copy)
@@ -1,5 +1,5 @@
 /*
- *      Copyright (C) 2005-2008 Team XBMC
+ *      Copyright (C) 2005-2009 Team XBMC
  *      http://www.xbmc.org
  *
  *  This Program is free software; you can redistribute it and/or modify
@@ -21,15 +21,22 @@
 #pragma once
 
 #include "StdString.h"
+#include "utils/StreamDetails.h"
 
 class CFileItem;
+class CDVDDemux;
 class CDVDFileInfo
 {
 public:
-  static bool ExtractThumb(const CStdString &strPath, const CStdString &strTarget);
+  // Extract a thumbnail immage from the media at strPath an image file in strTarget, optionally populating a streamdetails class with the data
+  static bool ExtractThumb(const CStdString &strPath, const CStdString &strTarget, CStreamDetails *pStreamDetails);
 
   // GetFileMetaData will fill pItem's properties according to what can be extracted from the file.
   static void GetFileMetaData(const CStdString &strPath, CFileItem *pItem);
+  
+  // Probe the files streams and store the info in the VideoInfoTag
+  static bool GetFileStreamDetails(CFileItem *pItem);
+  static bool DemuxerToStreamDetails(CDVDDemux *pDemux, CStreamDetails &details);
 
   static bool GetFileDuration(const CStdString &path, int &duration);
 };
Index: xbmc/cores/dvdplayer/DVDPlayer.cpp
===================================================================
--- xbmc/cores/dvdplayer/DVDPlayer.cpp	(revision 21009)
+++ xbmc/cores/dvdplayer/DVDPlayer.cpp	(working copy)
@@ -36,6 +36,8 @@
 #include "DVDCodecs/DVDCodecs.h"
 #include "DVDCodecs/DVDFactoryCodec.h"
 
+#include "DVDFileInfo.h"
+
 #include "Util.h"
 #include "utils/GUIInfoManager.h"
 #include "Application.h"
@@ -3026,6 +3028,52 @@
   return false;
 }
 
+int CDVDPlayer::GetChannels()
+{
+  if (m_pDemuxer && (m_CurrentAudio.id != -1))
+  {
+    CDemuxStreamAudio* stream = static_cast<CDemuxStreamAudio*>(m_pDemuxer->GetStream(m_CurrentAudio.id));
+    if (stream)
+      return stream->iChannels;
+  }
+  return -1;
+}
+
+CStdString CDVDPlayer::GetAudioCodecName()
+{
+  CStdString retVal;
+  if (m_pDemuxer && (m_CurrentAudio.id != -1))
+    m_pDemuxer->GetStreamCodecName(m_CurrentAudio.id, retVal);
+  return retVal;
+}
+
+CStdString CDVDPlayer::GetVideoCodecName()
+{
+  CStdString retVal;
+  if (m_pDemuxer && (m_CurrentVideo.id != -1))
+    m_pDemuxer->GetStreamCodecName(m_CurrentVideo.id, retVal);
+  return retVal;
+}
+
+int CDVDPlayer::GetPictureWidth()
+{
+  if (m_pDemuxer && (m_CurrentVideo.id != -1))
+  {
+    CDemuxStreamVideo* stream = static_cast<CDemuxStreamVideo*>(m_pDemuxer->GetStream(m_CurrentVideo.id));
+    if (stream)
+      return stream->iHeight;
+  }
+  return 0;
+}
+
+bool CDVDPlayer::GetStreamDetails(CStreamDetails &details)
+{
+  if (m_pDemuxer)
+    return CDVDFileInfo::DemuxerToStreamDetails(m_pDemuxer, details);
+  else
+    return false;
+}
+
 CDVDPlayer::CPlayerSeek::CPlayerSeek(CDVDPlayer* player)
       : m_player(*player)
 {
Index: xbmc/cores/dvdplayer/DVDPlayer.h
===================================================================
--- xbmc/cores/dvdplayer/DVDPlayer.h	(revision 21009)
+++ xbmc/cores/dvdplayer/DVDPlayer.h	(working copy)
@@ -192,6 +192,11 @@
   virtual int GetAudioBitrate();
   virtual int GetVideoBitrate();
   virtual int GetSourceBitrate();
+  virtual int GetChannels();
+  virtual CStdString GetAudioCodecName();
+  virtual CStdString GetVideoCodecName();
+  virtual int GetPictureWidth();
+  virtual bool GetStreamDetails(CStreamDetails &details);
 
   virtual bool GetCurrentSubtitle(CStdString& strSubtitle);
 
Index: xbmc/cores/IPlayer.h
===================================================================
--- xbmc/cores/IPlayer.h	(revision 21009)
+++ xbmc/cores/IPlayer.h	(working copy)
@@ -23,6 +23,7 @@
 
 #include "IAudioCallback.h"
 #include "Key.h"
+#include "utils/StreamDetails.h"
 
 class TiXmlElement;
 
@@ -125,7 +126,10 @@
   virtual int GetChannels(){ return 0;};
   virtual int GetBitsPerSample(){ return 0;};
   virtual int GetSampleRate(){ return 0;};
-  virtual CStdString GetCodecName(){ return "";};
+  virtual CStdString GetAudioCodecName(){ return "";}
+  virtual CStdString GetVideoCodecName(){ return "";}
+  virtual int GetPictureWidth(){ return 0;}
+  virtual bool GetStreamDetails(CStreamDetails &details){ return false;}
   virtual void ToFFRW(int iSpeed = 0){};
   // Skip to next track/item inside the current media (if supported).
   virtual bool SkipNext(){return false;}
Index: xbmc/cores/paplayer/PAPlayer.cpp
===================================================================
--- xbmc/cores/paplayer/PAPlayer.cpp	(revision 21009)
+++ xbmc/cores/paplayer/PAPlayer.cpp	(working copy)
@@ -803,7 +803,7 @@
   return 0;
 }
 
-CStdString PAPlayer::GetCodecName()
+CStdString PAPlayer::GetAudioCodecName()
 {
   ICodec* codec = m_decoder[m_currentDecoder].GetCodec();
   if (codec)
Index: xbmc/cores/paplayer/PAPlayer.h
===================================================================
--- xbmc/cores/paplayer/PAPlayer.h	(revision 21009)
+++ xbmc/cores/paplayer/PAPlayer.h	(working copy)
@@ -86,7 +86,7 @@
   virtual int GetChannels();
   virtual int GetBitsPerSample();
   virtual int GetSampleRate();
-  virtual CStdString GetCodecName();
+  virtual CStdString GetAudioCodecName();
   virtual __int64 GetTime();
   virtual void ResetTime();
   virtual void SeekTime(__int64 iTime = 0);
Index: xbmc/GUIWindowVideoBase.cpp
===================================================================
--- xbmc/GUIWindowVideoBase.cpp	(revision 21009)
+++ xbmc/GUIWindowVideoBase.cpp	(working copy)
@@ -78,6 +78,7 @@
     : CGUIMediaWindow(dwID, xmlFile)
 {
   m_thumbLoader.SetObserver(this);
+  m_thumbLoader.SetStreamDetailsObserver(this);
 }
 
 CGUIWindowVideoBase::~CGUIWindowVideoBase()
@@ -955,6 +956,20 @@
   CGUIMediaWindow::OnClick(iItem);
 }
 
+void CGUIWindowVideoBase::OnStreamDetails(const CStreamDetails &details, const CStdString &strFileName, long lFileId)
+{
+  CVideoDatabase db;
+  if (db.Open())
+  {
+    if (lFileId < 0)
+      db.SetStreamDetailsForFile(details, strFileName);
+    else
+      db.SetStreamDetailsForFileId(details, lFileId);
+
+    db.Close();
+  }
+}
+
 void CGUIWindowVideoBase::GetContextButtons(int itemNumber, CContextButtons &buttons)
 {
   CFileItemPtr item;
Index: xbmc/GUIWindowVideoBase.h
===================================================================
--- xbmc/GUIWindowVideoBase.h	(revision 21009)
+++ xbmc/GUIWindowVideoBase.h	(working copy)
@@ -26,7 +26,7 @@
 #include "PlayListPlayer.h"
 #include "ThumbLoader.h"
 
-class CGUIWindowVideoBase : public CGUIMediaWindow, public IBackgroundLoaderObserver
+class CGUIWindowVideoBase : public CGUIMediaWindow, public IBackgroundLoaderObserver, public IStreamDetailsObserver
 {
 public:
   CGUIWindowVideoBase(DWORD dwID, const CStdString &xmlFile);
@@ -40,6 +40,7 @@
   void AddToDatabase(int iItem);
   static void OnScan(const CStdString& strPath, const SScraperInfo& info, const VIDEO::SScanSettings& settings);
   virtual void OnInfo(CFileItem* pItem, const SScraperInfo& info);
+  virtual void OnStreamDetails(const CStreamDetails &details, const CStdString &strFileName, long lFileId);
   static void MarkUnWatched(const CFileItemPtr &pItem);
   static void MarkWatched(const CFileItemPtr &pItem);
   static void UpdateVideoTitle(const CFileItem* pItem);
Index: xbmc/GUIWindowVideoNav.cpp
===================================================================
--- xbmc/GUIWindowVideoNav.cpp	(revision 21009)
+++ xbmc/GUIWindowVideoNav.cpp	(working copy)
@@ -393,6 +393,18 @@
   }
 }
 
+void CGUIWindowVideoNav::OnItemLoaded(CFileItem* pItem)
+{
+  /* Notification from DVDFileInfo's scanner that either a thumb has been generated or stream
+     info is now present.  Setting the thumbnail automatically invalidates, but we need to
+     invalidate for new stream details */
+  if (pItem->IsVideoDb() && pItem->HasVideoInfoTag() && pItem->GetVideoInfoTag()->HasStreamDetails())
+  {
+    CUtil::DeleteVideoDatabaseDirectoryCache();
+    pItem->SetInvalid();
+  }    
+}
+
 bool CGUIWindowVideoNav::GetDirectory(const CStdString &strDirectory, CFileItemList &items)
 {
   if (m_bDisplayEmptyDatabaseMessage)
Index: xbmc/GUIWindowVideoNav.h
===================================================================
--- xbmc/GUIWindowVideoNav.h	(revision 21009)
+++ xbmc/GUIWindowVideoNav.h	(working copy)
@@ -43,7 +43,7 @@
   static bool DeleteItem(CFileItem* pItem, bool bUnavailable=false);
 
 protected:
-  virtual void OnItemLoaded(CFileItem* pItem) {};
+  virtual void OnItemLoaded(CFileItem* pItem);
   void OnLinkMovieToTvShow(int itemnumber, bool bRemove);
   // override base class methods
   virtual bool GetDirectory(const CStdString &strDirectory, CFileItemList &items);
Index: xbmc/ThumbLoader.cpp
===================================================================
--- xbmc/ThumbLoader.cpp	(revision 21009)
+++ xbmc/ThumbLoader.cpp	(working copy)
@@ -36,7 +36,8 @@
 using namespace XFILE;
 using namespace DIRECTORY;
 
-CThumbLoader::CThumbLoader()
+CThumbLoader::CThumbLoader(int nThreads) :
+  CBackgroundInfoLoader(nThreads)
 {
 }
 
@@ -65,7 +66,8 @@
   return pItem->HasThumbnail();
 }
 
-CVideoThumbLoader::CVideoThumbLoader()
+CVideoThumbLoader::CVideoThumbLoader() : 
+  CThumbLoader(), m_pStreamDetailsObs(NULL)
 {
 }
 
@@ -82,7 +84,7 @@
 {
 }
 
-bool CVideoThumbLoader::ExtractThumb(const CStdString &strPath, const CStdString &strTarget)
+bool CVideoThumbLoader::ExtractThumb(const CStdString &strPath, const CStdString &strTarget, CStreamDetails *pStreamDetails)
 {
   if (!g_guiSettings.GetBool("myvideos.autothumb"))
     return false;
@@ -96,12 +98,14 @@
     return false;
 
   CLog::Log(LOGDEBUG,"%s - trying to extract thumb from video file %s", __FUNCTION__, strPath.c_str());
-  return CDVDFileInfo::ExtractThumb(strPath, strTarget);
+  return CDVDFileInfo::ExtractThumb(strPath, strTarget, pStreamDetails);
 }
 
 bool CVideoThumbLoader::LoadItem(CFileItem* pItem)
 {
-  if (pItem->m_bIsShareOrDrive) return true;
+  if (pItem->m_bIsShareOrDrive) return false;
+
+  bool retVal = false;
   if (pItem->IsVideoDb() && pItem->HasVideoInfoTag() && !pItem->HasThumbnail())
   {
     if (pItem->m_bIsFolder && pItem->GetVideoInfoTag()->m_iSeason > -1)
@@ -114,6 +118,7 @@
       pItem->SetProperty("AutoThumbImage",item.GetProperty("AutoThumbImage"));
       pItem->SetProperty("fanart_image",item.GetProperty("fanart_image"));
       pItem->SetThumbnailImage(item.GetThumbnailImage());
+      pItem->GetVideoInfoTag()->m_streamDetails = item.GetVideoInfoTag()->m_streamDetails;
     }
     return bResult;
   }
@@ -131,15 +136,19 @@
       cachedThumb = strPath + "auto-" + strFileName;
       if (pItem->IsVideo() && !pItem->IsInternetStream() && !pItem->IsPlayList() && !CFile::Exists(cachedThumb))
       {
+        CStreamDetails details;
         if (pItem->IsStack())
         {
           CStackDirectory stack;
-          CVideoThumbLoader::ExtractThumb(stack.GetFirstStackedFile(pItem->m_strPath), cachedThumb);
+          CVideoThumbLoader::ExtractThumb(stack.GetFirstStackedFile(pItem->m_strPath), cachedThumb, &details);
         }
         else
         {
-          CVideoThumbLoader::ExtractThumb(pItem->m_strPath, cachedThumb);
+          CVideoThumbLoader::ExtractThumb(pItem->m_strPath, cachedThumb, &details);
         }
+
+        if (details.HasItems() && m_pStreamDetailsObs)
+          m_pStreamDetailsObs->OnStreamDetails(details, pItem->m_strPath, -1);
       }
 
       if (CFile::Exists(cachedThumb))
@@ -160,10 +169,21 @@
       pItem->SetProperty("fanart_image",pItem->GetCachedFanart());
   }
 
+  if (!pItem->m_bIsFolder && !pItem->IsInternetStream() && 
+    pItem->HasVideoInfoTag() && !pItem->GetVideoInfoTag()->HasStreamDetails())   
+  {
+    if (CDVDFileInfo::GetFileStreamDetails(pItem) && m_pStreamDetailsObs)
+    {
+      CVideoInfoTag *info = pItem->GetVideoInfoTag();
+      m_pStreamDetailsObs->OnStreamDetails(info->m_streamDetails, "", info->m_iFileId);
+      retVal = true;
+    }
+  }
+
 //  if (pItem->IsVideo() && !pItem->IsInternetStream())
 //    CDVDPlayer::GetFileMetaData(pItem->m_strPath, pItem);
 
-  return true;
+  return retVal;
 }
 
 CProgramThumbLoader::CProgramThumbLoader()
Index: xbmc/ThumbLoader.h
===================================================================
--- xbmc/ThumbLoader.h	(revision 21009)
+++ xbmc/ThumbLoader.h	(working copy)
@@ -22,11 +22,12 @@
 #ifndef THUMBLOADER_H
 #define THUMBLOADER_H
 #include "BackgroundInfoLoader.h"
+#include "utils/StreamDetails.h"
 
 class CThumbLoader : public CBackgroundInfoLoader
 {
 public:
-  CThumbLoader();
+  CThumbLoader(int nThreads=-1);
   virtual ~CThumbLoader();
 
   bool LoadRemoteThumb(CFileItem *pItem);
@@ -38,11 +39,14 @@
   CVideoThumbLoader();
   virtual ~CVideoThumbLoader();
   virtual bool LoadItem(CFileItem* pItem);
-  bool ExtractThumb(const CStdString &strPath, const CStdString &strTarget);
+  bool ExtractThumb(const CStdString &strPath, const CStdString &strTarget, CStreamDetails *pStreamDetails);
+  void SetStreamDetailsObserver(IStreamDetailsObserver *pObs) { m_pStreamDetailsObs = pObs; }
 
 protected:
   virtual void OnLoaderStart() ;
   virtual void OnLoaderFinish() ;
+
+  IStreamDetailsObserver *m_pStreamDetailsObs;
 };
 
 class CProgramThumbLoader : public CThumbLoader
Index: xbmc/utils/GUIInfoManager.cpp
===================================================================
--- xbmc/utils/GUIInfoManager.cpp	(revision 21009)
+++ xbmc/utils/GUIInfoManager.cpp	(working copy)
@@ -519,6 +519,11 @@
     else if (strTest.Equals("videoplayer.tagline")) return VIDEOPLAYER_TAGLINE;
     else if (strTest.Equals("videoplayer.hasinfo")) return VIDEOPLAYER_HAS_INFO;
     else if (strTest.Equals("videoplayer.trailer")) return VIDEOPLAYER_TRAILER;
+    else if (strTest.Equals("videoplayer.videocodec")) return VIDEOPLAYER_VIDEO_CODEC;
+    else if (strTest.Equals("videoplayer.videoresolution")) return VIDEOPLAYER_VIDEO_RESOLUTION;
+    else if (strTest.Equals("videoplayer.audiocodec")) return VIDEOPLAYER_AUDIO_CODEC;
+    else if (strTest.Equals("videoplayer.audiochannels")) return VIDEOPLAYER_AUDIO_CHANNELS;
+
   }
   else if (strCategory.Equals("playlist"))
   {
@@ -870,6 +875,13 @@
   else if (info.Equals("trailer")) return LISTITEM_TRAILER;
   else if (info.Equals("starrating")) return LISTITEM_STAR_RATING;
   else if (info.Equals("sortletter")) return LISTITEM_SORT_LETTER;
+  else if (info.Equals("videocodec")) return LISTITEM_VIDEO_CODEC;
+  else if (info.Equals("videoresolution")) return LISTITEM_VIDEO_RESOLUTION;
+  else if (info.Equals("videoaspect")) return LISTITEM_VIDEO_ASPECT;
+  else if (info.Equals("audiocodec")) return LISTITEM_AUDIO_CODEC;
+  else if (info.Equals("audiochannels")) return LISTITEM_AUDIO_CHANNELS;
+  else if (info.Equals("audiolanguage")) return LISTITEM_AUDIO_LANGUAGE;
+  else if (info.Equals("subtitlelanguage")) return LISTITEM_SUBTITLE_LANGUAGE;
   else if (info.Left(9).Equals("property(")) return AddListItemProp(info.Mid(9, info.GetLength() - 10));
   return 0;
 }
@@ -1037,6 +1049,22 @@
   case VIDEOPLAYER_TRAILER:
     strLabel = GetVideoLabel(info);
   break;
+  case VIDEOPLAYER_VIDEO_CODEC:
+    if(g_application.IsPlaying() && g_application.m_pPlayer)
+      strLabel = g_application.m_pPlayer->GetVideoCodecName();
+    break;
+  case VIDEOPLAYER_VIDEO_RESOLUTION:
+    if(g_application.IsPlaying() && g_application.m_pPlayer)
+      return VideoWidthToResolutionDescription(g_application.m_pPlayer->GetPictureWidth());
+    break;
+  case VIDEOPLAYER_AUDIO_CODEC:
+    if(g_application.IsPlaying() && g_application.m_pPlayer)
+      strLabel = g_application.m_pPlayer->GetAudioCodecName();
+    break;
+  case VIDEOPLAYER_AUDIO_CHANNELS:
+    if(g_application.IsPlaying() && g_application.m_pPlayer)
+      strLabel.Format("%i", g_application.m_pPlayer->GetChannels());
+    break;
   case PLAYLIST_LENGTH:
   case PLAYLIST_POSITION:
   case PLAYLIST_RANDOM:
@@ -2801,7 +2829,7 @@
   case MUSICPLAYER_CODEC:
     {
       CStdString strCodec;
-      strCodec.Format("%s", g_application.m_pPlayer->GetCodecName().c_str());
+      strCodec.Format("%s", g_application.m_pPlayer->GetAudioCodecName().c_str());
       return strCodec;
     }
     break;
@@ -3481,7 +3509,7 @@
   return item;
 }
 
-CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info ) const
+CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info) const
 {
   if (!item) return "";
 
@@ -3773,34 +3801,76 @@
       return letter;
     }
     break;
+  case LISTITEM_VIDEO_CODEC:
+    if (item->HasVideoInfoTag())
+      return item->GetVideoInfoTag()->m_streamDetails.GetVideoCodec();
+    break;
+  case LISTITEM_VIDEO_RESOLUTION:
+    if (item->HasVideoInfoTag())
+      return VideoWidthToResolutionDescription(item->GetVideoInfoTag()->m_streamDetails.GetVideoWidth());
+    break;
+  case LISTITEM_VIDEO_ASPECT:
+    if (item->HasVideoInfoTag())
+      return VideoAspectToAspectDescription(item->GetVideoInfoTag()->m_streamDetails.GetVideoAspect());
+    break;
+  case LISTITEM_AUDIO_CODEC:
+    if (item->HasVideoInfoTag())
+    {
+      return item->GetVideoInfoTag()->m_streamDetails.GetAudioCodec();
+    }
+    break;
+  case LISTITEM_AUDIO_CHANNELS:
+    if (item->HasVideoInfoTag())
+    {
+      CStdString strResult;
+      int iChannels = item->GetVideoInfoTag()->m_streamDetails.GetAudioChannels();
+      if (iChannels > -1)
+        strResult.Format("%i", iChannels);
+      return strResult;
+    }
+    break;
+  case LISTITEM_AUDIO_LANGUAGE:
+    if (item->HasVideoInfoTag())
+      return item->GetVideoInfoTag()->m_streamDetails.GetAudioLanguage();
+    break;
+  case LISTITEM_SUBTITLE_LANGUAGE:
+    if (item->HasVideoInfoTag())
+      return item->GetVideoInfoTag()->m_streamDetails.GetSubtitleLanguage();
+    break;
   }
   return "";
 }
 
 CStdString CGUIInfoManager::GetItemImage(const CFileItem *item, int info) const
 {
-  if (info == LISTITEM_RATING)
-  { // old song rating format
-    CStdString rating;
-    if (item->HasMusicInfoTag())
+  switch (info)
+  {
+  case LISTITEM_RATING:  // old song rating format
     {
-      rating.Format("songrating%c.png", item->GetMusicInfoTag()->GetRating());
+      CStdString rating;
+      if (item->HasMusicInfoTag())
+      {
+        rating.Format("songrating%c.png", item->GetMusicInfoTag()->GetRating());
+        return rating;
+      }
+    }
+    break;
+  case LISTITEM_STAR_RATING:
+    {
+      CStdString rating;
+      if (item->HasVideoInfoTag())
+      { // rating for videos is assumed 0..10, so convert to 0..5
+        rating.Format("rating%d.png", (long)((item->GetVideoInfoTag()->m_fRating * 0.5f) + 0.5f));
+      }
+      else if (item->HasMusicInfoTag())
+      { // song rating.
+        rating.Format("rating%c.png", item->GetMusicInfoTag()->GetRating());
+      }
       return rating;
     }
-  }
-  else if (info == LISTITEM_STAR_RATING)
-  {
-    CStdString rating;
-    if (item->HasVideoInfoTag())
-    { // rating for videos is assumed 0..10, so convert to 0..5
-      rating.Format("rating%d.png", (long)((item->GetVideoInfoTag()->m_fRating * 0.5f) + 0.5f));
-    }
-    else if (item->HasMusicInfoTag())
-    { // song rating.
-      rating.Format("rating%c.png", item->GetMusicInfoTag()->GetRating());
-    }
-    return rating;
-  }
+    break;
+  }  /* switch (info) */
+  
   return GetItemLabel(item, info);
 }
 
@@ -4011,6 +4081,60 @@
   m_currentFile->m_lStartOffset = 0;
 }
 
+CStdString CGUIInfoManager::VideoWidthToResolutionDescription(int iWidth) const
+{
+  if (iWidth == 0)
+    return "";
+
+  else if (iWidth < 721)
+    return "480";
+  // 960x540
+  else if (iWidth < 961)
+    return "540";
+  // 1280x720
+  else if (iWidth < 1281)
+    return "720";
+  // 1920x1080
+  else 
+    return "1080";
+}
+
+CStdString CGUIInfoManager::VideoAspectToAspectDescription(float fAspect) const
+{
+  const float VIDEOASPECT_EPSILON = 0.025f;
+  if (fAspect == 0.0f)
+    return "";
+
+  // With the epsilon method some of the ranges slightly overlap
+  // so go in increasing size order to minimize the impact
+  // of a growing tolerance value
+  float fTolerance = (fAspect * VIDEOASPECT_EPSILON);
+
+  // 4:3 video standard
+  if (fabs(fAspect - 1.33f) < fTolerance)
+    return "1.33";
+  // 1.66:1 35mm European flat
+  if (fabs(fAspect - 1.66f) < fTolerance)
+    return "1.66";
+  // 16:9 video widescreen 
+  if (fabs(fAspect - 1.77f) < fTolerance)
+    return "1.78";
+  // 1.85:1 35mm US flat (theatrical widescreen)
+  if (fabs(fAspect - 1.85f) < fTolerance)
+    return "1.85";
+  // 2.20:1 70m standard
+  if (fabs(fAspect - 2.20f) < fTolerance)
+    return "2.20";
+  // 2.35:1 anamorphic wide - included are both true 2.35 (pre 1970s) and new
+  // 2.39 as the industry convetion is to call the new standard 2.35 anyway
+  if (fabs(fAspect - 2.35f) < fTolerance)
+    return "2.35";
+  if (fabs(fAspect - 2.39f) < fTolerance)
+    return "2.35";
+
+  return "";
+}
+
 const CFileItem& CGUIInfoManager::GetCurrentSlide() const
 {
   return *m_currentSlide;
Index: xbmc/utils/GUIInfoManager.h
===================================================================
--- xbmc/utils/GUIInfoManager.h	(revision 21009)
+++ xbmc/utils/GUIInfoManager.h	(working copy)
@@ -242,6 +242,10 @@
 #define VIDEOPLAYER_TOP250            283
 #define VIDEOPLAYER_RATING_AND_VOTES  284
 #define VIDEOPLAYER_TRAILER           285
+#define VIDEOPLAYER_VIDEO_CODEC       286
+#define VIDEOPLAYER_VIDEO_RESOLUTION  287
+#define VIDEOPLAYER_AUDIO_CODEC       288
+#define VIDEOPLAYER_AUDIO_CHANNELS    289
 
 #define AUDIOSCROBBLER_ENABLED      300
 #define AUDIOSCROBBLER_CONN_STATE   301
@@ -465,6 +469,13 @@
 #define LISTITEM_SORT_LETTER        (LISTITEM_START + 43)
 #define LISTITEM_ALBUM_ARTIST       (LISTITEM_START + 44)
 #define LISTITEM_FOLDERNAME         (LISTITEM_START + 45)
+#define LISTITEM_VIDEO_CODEC        (LISTITEM_START + 46)
+#define LISTITEM_VIDEO_RESOLUTION   (LISTITEM_START + 47)
+#define LISTITEM_VIDEO_ASPECT       (LISTITEM_START + 48)
+#define LISTITEM_AUDIO_CODEC        (LISTITEM_START + 49)
+#define LISTITEM_AUDIO_CHANNELS     (LISTITEM_START + 50)
+#define LISTITEM_AUDIO_LANGUAGE     (LISTITEM_START + 51)
+#define LISTITEM_SUBTITLE_LANGUAGE  (LISTITEM_START + 52)
 
 #define LISTITEM_PROPERTY_START     (LISTITEM_START + 200)
 #define LISTITEM_PROPERTY_END       (LISTITEM_PROPERTY_START + 1000)
@@ -616,6 +627,8 @@
   TIME_FORMAT TranslateTimeFormat(const CStdString &format);
   CStdString LocalizeTime(const CDateTime &time, TIME_FORMAT format) const;
   bool GetItemBool(const CGUIListItem *item, int condition) const;
+  CStdString VideoWidthToResolutionDescription(int iWidth) const;
+  CStdString VideoAspectToAspectDescription(float fAspect) const;
 
   // Conditional string parameters for testing are stored in a vector for later retrieval.
   // The offset into the string parameters array is returned.
Index: xbmc/utils/Makefile
===================================================================
--- xbmc/utils/Makefile	(revision 21009)
+++ xbmc/utils/Makefile	(working copy)
@@ -53,7 +53,8 @@
      AsyncFileCopy.cpp \
      DbusServer.cpp \
      Atomics.cpp \
-     LockFree.cpp
+     LockFree.cpp \
+     StreamDetails.cpp
 
 LIB=utils.a
 
Index: xbmc/utils/StreamDetails.cpp
===================================================================
--- xbmc/utils/StreamDetails.cpp	(revision 0)
+++ xbmc/utils/StreamDetails.cpp	(revision 0)
@@ -0,0 +1,415 @@
+/*
+ *      Copyright (C) 2005-2009 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 "stdafx.h"
+#include "StreamDetails.h"
+
+void CStreamDetail::Serialize(CArchive &ar)
+{
+  // there's nothing to do here, the type is stored externally and parent isn't stored
+}
+
+CStreamDetailVideo::CStreamDetailVideo() :
+  m_iWidth(0), m_iHeight(0), m_fAspect(0.0), CStreamDetail(CStreamDetail::VIDEO)
+{
+}
+
+void CStreamDetailVideo::Serialize(CArchive& ar)
+{
+  CStreamDetail::Serialize(ar);
+  if (ar.IsStoring())
+  {
+    ar << m_strCodec;
+    ar << m_fAspect;
+    ar << m_iHeight;
+    ar << m_iWidth;
+  }
+  else
+  {
+    ar >> m_strCodec;
+    ar >> m_fAspect;
+    ar >> m_iHeight;
+    ar >> m_iWidth;
+  }
+}
+
+bool CStreamDetailVideo::IsWorseThan(CStreamDetail *that)
+{
+  if (that->m_eType != CStreamDetail::VIDEO)
+    return true;
+
+  // Best video stream is that with the most pixels
+  CStreamDetailVideo *sdv = (CStreamDetailVideo *)that;
+  return (sdv->m_iWidth * sdv->m_iHeight) > (m_iWidth * m_iHeight);
+}
+
+CStreamDetailAudio::CStreamDetailAudio() :
+  m_iChannels(-1), CStreamDetail(CStreamDetail::AUDIO)
+{
+}
+
+void CStreamDetailAudio::Serialize(CArchive& ar)
+{
+  CStreamDetail::Serialize(ar);
+  if (ar.IsStoring())
+  {
+    ar << m_strCodec;
+    ar << m_strLanguage;
+    ar << m_iChannels;
+  }
+  else
+  {
+    ar >> m_strCodec;
+    ar >> m_strLanguage;
+    ar >> m_iChannels;
+  }
+}
+
+int CStreamDetailAudio::GetCodecPriority() const
+{
+  if (m_strCodec == "eac3")
+    return 3;
+  if (m_strCodec == "dca")
+    return 2;
+  if (m_strCodec == "ac3")
+    return 1;
+  return 0;
+}
+
+bool CStreamDetailAudio::IsWorseThan(CStreamDetail *that)
+{
+  if (that->m_eType != CStreamDetail::AUDIO)
+    return true;
+
+  CStreamDetailAudio *sda = (CStreamDetailAudio *)that;
+  // First choice is the thing with the most channels
+  if (sda->m_iChannels > m_iChannels)
+    return true;
+  if (m_iChannels > sda->m_iChannels)
+    return false;
+
+  // In case of a tie, eac3 > dts > ac3 > all else.
+  return sda->GetCodecPriority() > GetCodecPriority();
+}
+
+CStreamDetailSubtitle::CStreamDetailSubtitle() :
+  CStreamDetail(CStreamDetail::SUBTITLE)
+{
+}
+
+void CStreamDetailSubtitle::Serialize(CArchive& ar)
+{
+  CStreamDetail::Serialize(ar);
+  if (ar.IsStoring())
+  {
+    ar << m_strLanguage;
+  }
+  else
+  {
+    ar >> m_strLanguage;
+  }
+}
+
+bool CStreamDetailSubtitle::IsWorseThan(CStreamDetail *that)
+{
+  if (that->m_eType != CStreamDetail::SUBTITLE)
+    return true;
+
+  // the preferred subtitle should be the one in the user's language
+  if (m_pParent)
+    if (m_pParent->m_strLanguage == m_strLanguage)
+      return false;  // already the best
+    else
+      return (m_pParent->m_strLanguage == ((CStreamDetailSubtitle *)that)->m_strLanguage);
+  return false;
+}
+
+CStreamDetails& CStreamDetails::operator=(const CStreamDetails &that)
+{
+  if (this != &that)
+  {
+    Reset();
+    std::vector<CStreamDetail *>::const_iterator iter;
+    for (iter = that.m_vecItems.begin(); iter != that.m_vecItems.end(); iter++)
+    {
+      switch ((*iter)->m_eType) 
+      {
+      case CStreamDetail::VIDEO:
+        AddStream(new CStreamDetailVideo((const CStreamDetailVideo &)(**iter)));
+        break;
+      case CStreamDetail::AUDIO:
+        AddStream(new CStreamDetailAudio((const CStreamDetailAudio &)(**iter)));
+        break;
+      case CStreamDetail::SUBTITLE:
+        AddStream(new CStreamDetailSubtitle((const CStreamDetailSubtitle &)(**iter)));
+        break;
+      }
+    }
+  
+    DetermineBestStreams();
+  }  /* if this != that */
+
+  return *this;
+}
+
+CStreamDetail *CStreamDetails::NewStream(CStreamDetail::StreamType type)
+{
+  CStreamDetail *retVal = NULL;
+  switch (type)
+  {
+  case CStreamDetail::VIDEO:
+    retVal = new CStreamDetailVideo();
+    break;
+  case CStreamDetail::AUDIO:
+    retVal = new CStreamDetailAudio();
+    break;
+  case CStreamDetail::SUBTITLE:
+    retVal = new CStreamDetailSubtitle();
+    break;
+  }
+
+  if (retVal)
+    AddStream(retVal);
+
+  return retVal;
+}
+
+const int CStreamDetails::GetStreamCount(CStreamDetail::StreamType type) const
+{
+  int retVal = 0;
+  std::vector<CStreamDetail *>::const_iterator iter;
+  for (iter = m_vecItems.begin(); iter != m_vecItems.end(); iter++)
+    if ((*iter)->m_eType == type)
+      retVal++;
+  return retVal;
+}
+
+const int CStreamDetails::GetVideoStreamCount(void) const
+{
+  return GetStreamCount(CStreamDetail::VIDEO);
+}
+
+const int CStreamDetails::GetAudioStreamCount(void) const
+{
+  return GetStreamCount(CStreamDetail::AUDIO);
+}
+
+const int CStreamDetails::GetSubtitleStreamCount(void) const
+{
+  return GetStreamCount(CStreamDetail::SUBTITLE);
+}
+
+CStreamDetails::CStreamDetails(const CStreamDetails &that)
+{
+  *this = that;
+}
+
+void CStreamDetails::AddStream(CStreamDetail *item)
+{
+  item->m_pParent = this;
+  m_vecItems.push_back(item);
+}
+
+void CStreamDetails::Reset(void)
+{
+  m_pBestVideo = NULL;
+  m_pBestAudio = NULL;
+  m_pBestSubtitle = NULL;
+
+  std::vector<CStreamDetail *>::iterator iter;
+  for (iter = m_vecItems.begin(); iter != m_vecItems.end(); iter++)
+    delete *iter;
+  m_vecItems.clear();
+}
+
+const CStreamDetail* CStreamDetails::GetNthStream(CStreamDetail::StreamType type, int idx) const
+{
+  if (idx == 0)
+  {
+    switch (type)
+    {
+    case CStreamDetail::VIDEO:
+      return m_pBestVideo; 
+      break;
+    case CStreamDetail::AUDIO:
+      return m_pBestAudio; 
+      break;
+    case CStreamDetail::SUBTITLE:
+      return m_pBestSubtitle; 
+      break;
+    default:
+      return NULL;
+      break;
+    }
+  }
+
+  std::vector<CStreamDetail *>::const_iterator iter;
+  for (iter = m_vecItems.begin(); iter != m_vecItems.end(); iter++)
+    if ((*iter)->m_eType == type)
+    {
+      idx--;
+      if (idx < 1)
+        return *iter;
+    }
+
+  return NULL;
+}
+
+CStdString CStreamDetails::GetVideoCodec(int idx) const
+{
+  CStreamDetailVideo *item = (CStreamDetailVideo *)GetNthStream(CStreamDetail::VIDEO, idx);
+  if (item)
+    return item->m_strCodec;
+  else
+    return "";
+}
+
+float CStreamDetails::GetVideoAspect(int idx) const
+{
+  CStreamDetailVideo *item = (CStreamDetailVideo *)GetNthStream(CStreamDetail::VIDEO, idx);
+  if (item)
+    return item->m_fAspect;
+  else
+    return 0.0;
+}
+
+int CStreamDetails::GetVideoWidth(int idx) const
+{
+  CStreamDetailVideo *item = (CStreamDetailVideo *)GetNthStream(CStreamDetail::VIDEO, idx);
+  if (item)
+    return item->m_iWidth;
+  else
+    return 0;
+}
+
+int CStreamDetails::GetVideoHeight(int idx) const
+{
+  CStreamDetailVideo *item = (CStreamDetailVideo *)GetNthStream(CStreamDetail::VIDEO, idx);
+  if (item)
+    return item->m_iHeight;
+  else
+    return 0;
+}
+
+CStdString CStreamDetails::GetAudioCodec(int idx) const
+{
+  CStreamDetailAudio *item = (CStreamDetailAudio *)GetNthStream(CStreamDetail::AUDIO, idx);
+  if (item)
+    return item->m_strCodec;
+  else
+    return "";
+}
+
+CStdString CStreamDetails::GetAudioLanguage(int idx) const
+{
+  CStreamDetailAudio *item = (CStreamDetailAudio *)GetNthStream(CStreamDetail::AUDIO, idx);
+  if (item)
+    return item->m_strLanguage;
+  else
+    return "";
+}
+
+int CStreamDetails::GetAudioChannels(int idx) const
+{
+  CStreamDetailAudio *item = (CStreamDetailAudio *)GetNthStream(CStreamDetail::AUDIO, idx);
+  if (item)
+    return item->m_iChannels;
+  else
+    return -1;
+}
+
+CStdString CStreamDetails::GetSubtitleLanguage(int idx) const
+{
+  CStreamDetailSubtitle *item = (CStreamDetailSubtitle *)GetNthStream(CStreamDetail::SUBTITLE, idx);
+  if (item)
+    return item->m_strLanguage;
+  else
+    return "";
+}
+
+void CStreamDetails::Serialize(CArchive& ar)
+{
+  if (ar.IsStoring())
+  {
+    ar << (int)m_vecItems.size();
+
+    std::vector<CStreamDetail *>::const_iterator iter;
+    for (iter = m_vecItems.begin(); iter != m_vecItems.end(); iter++)
+    {
+      // the type goes before the actual item.  When loading we need
+      // to know the type before we can construct an instance to serialize
+      ar << (int)(*iter)->m_eType;
+      ar << (**iter);
+    }
+  }
+  else
+  {
+    int count;
+    ar >> count;
+
+    Reset();
+    for (int i=0; i<count; i++)
+    {
+      CStreamDetail::StreamType type;
+      CStreamDetail *p = NULL;
+
+      ar >> (int &)type;
+      p = NewStream(type);
+      if (p)
+        ar >> (*p);
+    }
+
+    DetermineBestStreams();
+  }
+}
+
+void CStreamDetails::DetermineBestStreams(void)
+{
+  m_pBestVideo = NULL;
+  m_pBestAudio = NULL;
+  m_pBestSubtitle = NULL;
+
+  std::vector<CStreamDetail *>::const_iterator iter;
+  for (iter = m_vecItems.begin(); iter != m_vecItems.end(); iter++)
+  {
+    CStreamDetail **champion;
+    switch ((*iter)->m_eType)
+    {
+    case CStreamDetail::VIDEO:
+      champion = (CStreamDetail **)&m_pBestVideo; 
+      break;
+    case CStreamDetail::AUDIO:
+      champion = (CStreamDetail **)&m_pBestAudio; 
+      break;
+    case CStreamDetail::SUBTITLE:
+      champion = (CStreamDetail **)&m_pBestSubtitle; 
+      break;
+    default:
+      champion = NULL;
+    }  /* switch type */
+
+    if (!champion)
+      continue;
+
+    if ((*champion == NULL) || (*champion)->IsWorseThan(*iter))
+      *champion = *iter;
+  }  /* for each */
+}
Index: xbmc/utils/StreamDetails.h
===================================================================
--- xbmc/utils/StreamDetails.h	(revision 0)
+++ xbmc/utils/StreamDetails.h	(revision 0)
@@ -0,0 +1,132 @@
+#pragma once
+/*
+ *      Copyright (C) 2005-2008 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 "Archive.h"
+#include <vector>
+
+class CStreamDetails;
+
+class CStreamDetail : public ISerializable
+{
+public:
+  enum StreamType {
+    VIDEO,
+    AUDIO,
+    SUBTITLE
+  };
+
+  CStreamDetail(StreamType type) : m_eType(type) {};
+  virtual void Serialize(CArchive& ar);
+  virtual bool IsWorseThan(CStreamDetail *that) { return true; };
+
+  const StreamType m_eType;
+
+protected:
+  CStreamDetails *m_pParent;
+  friend class CStreamDetails;
+};
+
+class CStreamDetailVideo : public CStreamDetail
+{
+public:
+  CStreamDetailVideo();
+  virtual void Serialize(CArchive& ar);
+  virtual bool IsWorseThan(CStreamDetail *that);
+
+  int m_iWidth;
+  int m_iHeight;
+  float m_fAspect;
+  CStdString m_strCodec;
+};
+
+class CStreamDetailAudio : public CStreamDetail
+{
+public:
+  CStreamDetailAudio();
+  virtual void Serialize(CArchive& ar);
+  virtual bool IsWorseThan(CStreamDetail *that);
+
+  int m_iChannels;
+  CStdString m_strCodec;
+  CStdString m_strLanguage;
+private:
+  int GetCodecPriority() const;
+};
+
+class CStreamDetailSubtitle : public CStreamDetail
+{
+public:
+  CStreamDetailSubtitle();
+  virtual void Serialize(CArchive& ar);
+  virtual bool IsWorseThan(CStreamDetail *that);
+
+  CStdString m_strLanguage;
+};
+
+class CStreamDetails : public ISerializable
+{
+public:
+  CStreamDetails() { Reset(); };
+  CStreamDetails(const CStreamDetails &that);
+  ~CStreamDetails() { Reset(); };
+  CStreamDetails& operator=(const CStreamDetails &that);
+
+  const bool HasItems(void) const { return m_vecItems.size() > 0; };
+  const int GetStreamCount(CStreamDetail::StreamType type) const;
+  const int GetVideoStreamCount(void) const;
+  const int GetAudioStreamCount(void) const;
+  const int GetSubtitleStreamCount(void) const;
+  const CStreamDetail* GetNthStream(CStreamDetail::StreamType type, int idx) const;
+
+  CStdString GetVideoCodec(int idx = 0) const;
+  float GetVideoAspect(int idx = 0) const;
+  int GetVideoWidth(int idx = 0) const;
+  int GetVideoHeight(int idx = 0) const;
+
+  CStdString GetAudioCodec(int idx = 0) const;
+  CStdString GetAudioLanguage(int idx = 0) const;
+  int GetAudioChannels(int idx = 0) const;
+
+  CStdString GetSubtitleLanguage(int idx = 0) const;
+
+  void AddStream(CStreamDetail *item);
+  void Reset(void);
+  void DetermineBestStreams(void);
+
+  virtual void Serialize(CArchive& ar);
+
+  // Language to use for "best" subtitle stream
+  CStdString m_strLanguage; 
+
+private:
+  CStreamDetail *NewStream(CStreamDetail::StreamType type);
+  std::vector<CStreamDetail *> m_vecItems;
+  CStreamDetailVideo *m_pBestVideo;
+  CStreamDetailAudio *m_pBestAudio;
+  CStreamDetailSubtitle *m_pBestSubtitle;
+};
+
+class IStreamDetailsObserver
+{
+public:
+  virtual void OnStreamDetails(const CStreamDetails &details, const CStdString &strFileName, long lFileId) = 0;
+};
Index: xbmc/VideoDatabase.cpp
===================================================================
--- xbmc/VideoDatabase.cpp	(revision 21009)
+++ xbmc/VideoDatabase.cpp	(working copy)
@@ -44,7 +44,7 @@
 using namespace DIRECTORY;
 using namespace VIDEO;
 
-#define VIDEO_DATABASE_VERSION 26
+#define VIDEO_DATABASE_VERSION 27
 #define VIDEO_DATABASE_OLD_VERSION 3.f
 #define VIDEO_DATABASE_NAME "MyVideos34.db"
 #define RECENTLY_ADDED_LIMIT  25
@@ -271,6 +271,12 @@
     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_1 ON directorlinkmusicvideo ( idDirector, idMVideo )\n");
     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_2 ON directorlinkmusicvideo ( idMVideo, idDirector )\n");
 
+    CLog::Log(LOGINFO, "create streaminfo table");
+    m_pDS->exec("CREATE TABLE streamdetails (idFile integer, iStreamType integer, "
+      "strVideoCodec text, fVideoAspect real, iVideoWidth integer, iVideoHeight integer, "
+      "strAudioCodec text, iAudioChannels integer, strAudioLanguage text, strSubtitleLanguage text)");
+    m_pDS->exec("CREATE INDEX ix_streamdetails ON streamdetails (idFile)");
+
     CLog::Log(LOGINFO, "create tvshowview");
     CStdString showview=FormatSQL("create view tvshowview as select tvshow.*,path.strPath as strPath,"
                                   "counts.totalcount as totalCount,counts.watchedcount as watchedCount,"
@@ -1737,6 +1743,9 @@
       AddActorToMovie(lMovieId, lActor, it->strRole);
     }
 
+    if (details.HasStreamDetails())
+      SetStreamDetailsForFileId(details.m_streamDetails, lFileId);
+
     // update our movie table (we know it was added already above)
     // and insert the new row
     CStdString sql = "update movie set " + GetValueString(info, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets);
@@ -1814,7 +1823,6 @@
   try
   {
     BeginTransaction();
-    
     if (lEpisodeId == -1)
     {
       lEpisodeId = GetEpisodeId(strFilenameAndPath);
@@ -1860,6 +1868,12 @@
       AddDirectorToEpisode(lEpisodeId, vecDirectors[i]);
     }
 
+    if (details.HasStreamDetails())
+      if (details.m_iFileId != -1)
+        SetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
+      else
+        SetStreamDetailsForFile(details.m_streamDetails, strFilenameAndPath);
+
     // and insert the new row
     CStdString sql = "update episode set " + GetValueString(details, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets);
     sql += FormatSQL("where idEpisode=%u", lEpisodeId);
@@ -1930,6 +1944,9 @@
       AddStudioToMusicVideo(lMVideoId, vecStudios[i]);
     }
 
+    if (details.HasStreamDetails())
+      SetStreamDetailsForFileId(details.m_streamDetails, lFileId);
+
     // update our movie table (we know it was added already above)
     // and insert the new row
     CStdString sql = "update musicvideo set " + GetValueString(details, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets);
@@ -1943,6 +1960,61 @@
   }
 }
 
+void CVideoDatabase::SetStreamDetailsForFile(const CStreamDetails& details, const CStdString &strFileNameAndPath)
+{
+  // AddFile checks to make sure the file isn't already in the DB first
+  long lFileId = AddFile(strFileNameAndPath);
+  if (lFileId < 0) 
+    return;
+  SetStreamDetailsForFileId(details, lFileId);
+}
+
+void CVideoDatabase::SetStreamDetailsForFileId(const CStreamDetails& details, long lFileId)
+{
+  if (lFileId < 0)
+    return;
+
+  try
+  {
+    BeginTransaction();
+    m_pDS->exec(FormatSQL("DELETE FROM streamdetails WHERE idFile = %u", lFileId));
+
+    for (int i=1; i<=details.GetVideoStreamCount(); i++)
+    {
+      m_pDS->exec(FormatSQL("INSERT INTO streamdetails "
+        "(idFile, iStreamType, strVideoCodec, fVideoAspect, iVideoWidth, iVideoHeight) "
+        "VALUES (%u,%i,'%s',%f,%i,%i)",
+        lFileId, (int)CStreamDetail::VIDEO,
+        details.GetVideoCodec(i).c_str(), details.GetVideoAspect(i),
+        details.GetVideoWidth(i), details.GetVideoHeight(i)));
+    }
+    for (int i=1; i<=details.GetAudioStreamCount(); i++)
+    {
+      m_pDS->exec(FormatSQL("INSERT INTO streamdetails "
+        "(idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage) "
+        "VALUES (%u,%i,'%s',%i,'%s')",
+        lFileId, (int)CStreamDetail::AUDIO,
+        details.GetAudioCodec(i).c_str(), details.GetAudioChannels(i),
+        details.GetAudioLanguage(i).c_str()));
+    }
+    for (int i=1; i<=details.GetSubtitleStreamCount(); i++)
+    {
+      m_pDS->exec(FormatSQL("INSERT INTO streamdetails "
+        "(idFile, iStreamType, strSubtitleLanguage) "
+        "VALUES (%u,%i,'%s')",
+        lFileId, (int)CStreamDetail::SUBTITLE,
+        details.GetSubtitleLanguage(i).c_str()));
+    }
+
+    CommitTransaction();
+  }
+  catch (...)
+  {
+    RollbackTransaction();
+    CLog::Log(LOGERROR, "%s (%u) failed", __FUNCTION__, lFileId);
+  }
+}
+
 //********************************************************************************************************************************
 void CVideoDatabase::GetFilePathById(long lMovieId, CStdString &filePath, VIDEODB_CONTENT_TYPE iType)
 {
@@ -2267,6 +2339,10 @@
       return ;
     }
 
+    long lFileId = GetFileId(strFilenameAndPath);
+    if (lFileId < 0)
+      return ;
+
     BeginTransaction();
 
     CStdString strSQL;
@@ -2285,6 +2361,8 @@
     if (!bKeepThumb)
       DeleteThumbForItem(strFilenameAndPath,false);
 
+    DeleteStreamDetails(lFileId);
+
     // keep the movie table entry, linking to tv shows, and bookmarks
     // so we can update the data in place
     // the ancilliary tables are still purged
@@ -2426,6 +2504,8 @@
     if (!bKeepThumb)
       DeleteThumbForItem(strFilenameAndPath,false);
 
+    DeleteStreamDetails(lFileId);
+
     // keep episode table entry and bookmarks so we can update the data in place
     // the ancilliary tables are still purged
     if (!bKeepId)
@@ -2464,6 +2544,10 @@
       return ;
     }
 
+    long lFileId = GetFileId(strFilenameAndPath);
+    if (lFileId < 0)
+      return ;
+ 
     BeginTransaction();
 
     CStdString strSQL;
@@ -2482,6 +2566,8 @@
     if (!bKeepThumb)
       DeleteThumbForItem(strFilenameAndPath,false);
 
+    DeleteStreamDetails(lFileId);
+
     // keep the music video table entry and bookmarks so we can update data in place
     // the ancilliary tables are still purged
     if (!bKeepId)
@@ -2512,6 +2598,11 @@
   }
 }
 
+void CVideoDatabase::DeleteStreamDetails(long lFileId)
+{
+    m_pDS->exec(FormatSQL("delete from streamdetails where idFile=%u", lFileId));
+}
+
 void CVideoDatabase::GetDetailsFromDB(auto_ptr<Dataset> &pDS, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details)
 {
   for (int i = min + 1; i < max; i++)
@@ -2561,6 +2652,66 @@
   return details;
 }
 
+bool CVideoDatabase::GetStreamDetailsForFileId(CStreamDetails& details, long lFileId) const
+{
+  if (lFileId < 0)
+    return false;
+
+  bool retVal = false;
+
+  dbiplus::Dataset *pDS = m_pDB->CreateDataset();
+  CStdString strSQL = FormatSQL("SELECT * FROM streamdetails WHERE idFile = %u", lFileId);
+  pDS->query(strSQL);
+
+  details.Reset();
+  while (!pDS->eof())
+  {
+    CStreamDetail::StreamType e = (CStreamDetail::StreamType)pDS->fv(1).get_asInteger();
+    switch (e)
+    {
+    case CStreamDetail::VIDEO:
+      {
+        CStreamDetailVideo *p = new CStreamDetailVideo();
+        p->m_strCodec = pDS->fv(2).get_asString();
+        p->m_fAspect = pDS->fv(3).get_asFloat();
+        p->m_iWidth = pDS->fv(4).get_asInteger();
+        p->m_iHeight = pDS->fv(5).get_asInteger();
+        details.AddStream(p);
+        retVal = true;
+        break;
+      }
+    case CStreamDetail::AUDIO:
+      {
+        CStreamDetailAudio *p = new CStreamDetailAudio();
+        p->m_strCodec = pDS->fv(6).get_asString();
+        if (pDS->fv(7).get_isNull())
+          p->m_iChannels = -1;
+        else
+          p->m_iChannels = pDS->fv(7).get_asInteger();
+        p->m_strLanguage = pDS->fv(8).get_asString();
+        details.AddStream(p);
+        retVal = true;
+        break;
+      }
+    case CStreamDetail::SUBTITLE:
+      {
+        CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
+        p->m_strLanguage = pDS->fv(9).get_asString();
+        details.AddStream(p);
+        retVal = true;
+        break;
+      }
+    }
+
+    pDS->next();
+  }
+
+  pDS->close();
+  details.DetermineBestStreams();
+
+  return retVal;
+}
+
 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(auto_ptr<Dataset> &pDS, bool needsCast /* = false */)
 {
   CVideoInfoTag details;
@@ -2572,10 +2723,11 @@
   GetDetailsFromDB(pDS, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets, details);
 
   details.m_iDbId = lMovieId;
-
   GetCommonDetails(pDS, details);
   movieTime += timeGetTime() - time; time = timeGetTime();
 
+  GetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
+
   if (needsCast)
   {
     // create cast string
@@ -2644,12 +2796,13 @@
 
   GetDetailsFromDB(pDS, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets, details);
   details.m_iDbId = lEpisodeId;
-
   GetCommonDetails(pDS, details);
   movieTime += timeGetTime() - time; time = timeGetTime();
 
   details.m_strShowTitle = pDS->fv(VIDEODB_DETAILS_EPISODE_TVSHOW_NAME).get_asString();
 
+  GetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
+
   if (needsCast)
   {
     // create cast string
@@ -2695,16 +2848,18 @@
 
   GetDetailsFromDB(pDS, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets, details);
   details.m_iDbId = lMovieId;
-
   GetCommonDetails(pDS, details);
   movieTime += timeGetTime() - time; time = timeGetTime();
 
+  GetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
+
   details.m_strPictureURL.Parse();
   return details;
 }
 
 void CVideoDatabase::GetCommonDetails(auto_ptr<Dataset> &pDS, CVideoInfoTag &details)
 {
+  details.m_iFileId = pDS->fv(VIDEODB_DETAILS_FILEID).get_asLong();
   details.m_strPath = pDS->fv(VIDEODB_DETAILS_PATH).get_asString();
   CStdString strFileName = pDS->fv(VIDEODB_DETAILS_FILE).get_asString();
   ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
@@ -3397,6 +3552,14 @@
       m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_1 ON studiolinktvshow ( idStudio, idShow)\n");
       m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_2 ON studiolinktvshow ( idShow, idStudio)\n");
     }
+    if (iVersion < 27)
+    {
+      // Add the streamdetail table
+      m_pDS->exec("CREATE TABLE streamdetails (idFile integer, iStreamType integer, "
+        "strVideoCodec text, fVideoAspect read, iVideoWidth integer, iVideoHeight integer, "
+        "strAudioCodec text, iAudioChannels integer, strAudioLanguage text, strSubtitleLanguage text)");
+      m_pDS->exec("CREATE INDEX ix_streamdetails ON streamdetails (idFile)");
+    }
   }
   catch (...)
   {
@@ -3859,7 +4022,8 @@
         pItem->SetLabelPreformated(true);
         if (!items.Contains(pItem->m_strPath))
         {
-          CStdString strThumb = CUtil::GetCachedAlbumThumb(pItem->GetLabel(),it->second.second);
+          pItem->GetVideoInfoTag()->m_strArtist = m_pDS->fv(2).get_asString();
+          CStdString strThumb = CUtil::GetCachedAlbumThumb(pItem->GetLabel(),pItem->GetVideoInfoTag()->m_strArtist);
           if (CFile::Exists(strThumb))
             pItem->SetThumbnailImage(strThumb);
           items.Add(pItem);
@@ -3878,8 +4042,7 @@
         pItem->SetLabelPreformated(true);
         if (!items.Contains(pItem->m_strPath))
         {
-          pItem->GetVideoInfoTag()->m_strArtist = m_pDS->fv(2).get_asString();
-          CStdString strThumb = CUtil::GetCachedAlbumThumb(pItem->GetLabel(),pItem->GetVideoInfoTag()->m_strArtist);
+          CStdString strThumb = CUtil::GetCachedAlbumThumb(pItem->GetLabel(),m_pDS->fv(2).get_asString());
           if (CFile::Exists(strThumb))
             pItem->SetThumbnailImage(strThumb);
           items.Add(pItem);
@@ -4454,18 +4617,17 @@
     items.Reserve(iRowsFound);
     while (!m_pDS->eof())
     {
-      long lMovieId = m_pDS->fv("idMovie").get_asLong();
       CVideoInfoTag movie = GetDetailsForMovie(m_pDS);
       if (g_settings.m_vecProfiles[0].getLockMode() == LOCK_MODE_EVERYONE ||
           g_passwordManager.bMasterUser                                   ||
           g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, g_settings.m_videoSources))
       {
-        CFileItemPtr pItem(new CFileItem(movie));
-        pItem->m_strPath.Format("%s%ld", strBaseDir.c_str(), lMovieId);
-        pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED,movie.m_playCount > 0);
-        items.Add(pItem);
+        CFileItemPtr pItem(new CFileItem(movie));  
+        pItem->m_strPath.Format("%s%ld", strBaseDir.c_str(), movie.m_iDbId);  
+        pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED,movie.m_playCount > 0);  
+        items.Add(pItem);  
       }
-      m_pDS->next();
+      m_pDS->next();  // 4%
     }
 
     CLog::Log(LOGDEBUG,"Time to retrieve movies from dataset = %d",
@@ -6183,6 +6345,10 @@
     sql = "delete from files where idFile in " + filesToDelete;
     m_pDS->exec(sql.c_str());
 
+    CLog::Log(LOGDEBUG, "%s Cleaning streamdetails table", __FUNCTION__);
+    sql = "delete from streamdetails where idFile in " + filesToDelete;
+    m_pDS->exec(sql.c_str());
+
     CLog::Log(LOGDEBUG, "%s Cleaning bookmark table", __FUNCTION__);
     sql = "delete from bookmark where idFile in " + filesToDelete;
     m_pDS->exec(sql.c_str());
@@ -7112,3 +7278,4 @@
 }
 
 
+
Index: xbmc/VideoDatabase.h
===================================================================
--- xbmc/VideoDatabase.h	(revision 21009)
+++ xbmc/VideoDatabase.h	(working copy)
@@ -57,7 +57,8 @@
 
 // these defines are based on how many columns we have and which column certain data is going to be in
 // when we do GetDetailsForMovie()
-#define VIDEODB_MAX_COLUMNS 21 // leave room for the fileid
+#define VIDEODB_MAX_COLUMNS 21 
+#define VIDEODB_DETAILS_FILEID               VIDEODB_MAX_COLUMNS + 1
 #define VIDEODB_DETAILS_FILE                 VIDEODB_MAX_COLUMNS + 2
 #define VIDEODB_DETAILS_PATH                 VIDEODB_MAX_COLUMNS + 3
 #define VIDEODB_DETAILS_PLAYCOUNT            VIDEODB_MAX_COLUMNS + 4
@@ -331,6 +332,7 @@
   void GetTvShowInfo(const CStdString& strPath, CVideoInfoTag& details, long lTvShowId = -1);
   bool GetEpisodeInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, long lEpisodeId = -1);
   void GetMusicVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, long idMVideo=-1);
+  bool GetStreamDetailsForFileId(CStreamDetails& details, long lFileId) const;
 
   long GetPathId(const CStdString& strPath);
   long GetTvShowId(const CStdString& strPath);
@@ -342,6 +344,8 @@
   long SetDetailsForTvShow(const CStdString& strPath, const CVideoInfoTag& details);
   long SetDetailsForEpisode(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, long idShow, long lEpisodeId=-1);
   void SetDetailsForMusicVideo(const CStdString& strFilenameAndPath, const CVideoInfoTag& details);
+  void SetStreamDetailsForFile(const CStreamDetails& details, const CStdString &strFileNameAndPath);
+  void SetStreamDetailsForFileId(const CStreamDetails& details, long lFileId);
 
   void DeleteMovie(const CStdString& strFilenameAndPath, bool bKeepId = false, bool bKeepThumb = false);
   void DeleteTvShow(const CStdString& strPath, bool bKeepId = false, bool bKeepThumb = false);
@@ -504,6 +508,7 @@
   void AddGenreAndDirectorsAndStudios(const CVideoInfoTag& details, std::vector<long>& vecDirectors, std::vector<long>& vecGenres, std::vector<long>& vecStudios);
 
   int GetPlayCount(long id);
+  void DeleteStreamDetails(long lFileId);
   CVideoInfoTag GetDetailsByTypeAndId(VIDEODB_CONTENT_TYPE type, long id);
   CVideoInfoTag GetDetailsForMovie(std::auto_ptr<dbiplus::Dataset> &pDS, bool needsCast = false);
   CVideoInfoTag GetDetailsForTvShow(std::auto_ptr<dbiplus::Dataset> &pDS, bool needsCast = false);
Index: xbmc/VideoInfoTag.cpp
===================================================================
--- xbmc/VideoInfoTag.cpp	(revision 21009)
+++ xbmc/VideoInfoTag.cpp	(working copy)
@@ -65,11 +65,13 @@
   m_iSpecialSortEpisode = -1;
   m_fRating = 0.0f;
   m_iDbId = -1;
+  m_iFileId = -1;
   m_iBookmarkId = -1;
   m_iTrack = -1;
   m_fanart.m_xml = "";
   m_strRuntime = "";
   m_lastPlayed = "";
+  m_streamDetails.Reset();
   m_playCount = 0;
 }
 
@@ -136,6 +138,38 @@
   XMLUtils::SetString(movie, "studio", m_strStudio);
   XMLUtils::SetString(movie, "trailer", m_strTrailer);
 
+  if (m_streamDetails.HasItems())
+  {
+    // it goes fileinfo/streamdetails/[video|audio|subtitle]
+    TiXmlElement fileinfo("fileinfo");
+    TiXmlElement streamdetails("streamdetails");
+    for (int iStream=1; iStream<=m_streamDetails.GetVideoStreamCount(); iStream++)
+    {
+      TiXmlElement stream("video");
+      XMLUtils::SetString(&stream, "codec", m_streamDetails.GetVideoCodec(iStream));
+      XMLUtils::SetFloat(&stream, "aspect", m_streamDetails.GetVideoAspect(iStream));
+      XMLUtils::SetInt(&stream, "width", m_streamDetails.GetVideoWidth(iStream));
+      XMLUtils::SetInt(&stream, "height", m_streamDetails.GetVideoHeight(iStream));
+      streamdetails.InsertEndChild(stream);
+    }
+    for (int iStream=1; iStream<=m_streamDetails.GetAudioStreamCount(); iStream++)
+    {
+      TiXmlElement stream("audio");
+      XMLUtils::SetString(&stream, "codec", m_streamDetails.GetAudioCodec(iStream));
+      XMLUtils::SetString(&stream, "language", m_streamDetails.GetAudioLanguage(iStream));
+      XMLUtils::SetInt(&stream, "channels", m_streamDetails.GetAudioChannels(iStream));
+      streamdetails.InsertEndChild(stream);
+    }
+    for (int iStream=1; iStream<=m_streamDetails.GetSubtitleStreamCount(); iStream++)
+    {
+      TiXmlElement stream("subtitle");
+      XMLUtils::SetString(&stream, "language", m_streamDetails.GetSubtitleLanguage(iStream));
+      streamdetails.InsertEndChild(stream);
+    }
+    fileinfo.InsertEndChild(streamdetails);
+    movie->InsertEndChild(fileinfo);
+  }  /* if has stream details */
+
   // cast
   for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
   {
@@ -223,10 +257,12 @@
     ar << m_iEpisode;
     ar << m_fRating;
     ar << m_iDbId;
+    ar << m_iFileId;
     ar << m_iSpecialSortSeason;
     ar << m_iSpecialSortEpisode;
     ar << m_iBookmarkId;
     ar << m_iTrack;
+    ar << m_streamDetails;
   }
   else
   {
@@ -280,10 +316,12 @@
     ar >> m_iEpisode;
     ar >> m_fRating;
     ar >> m_iDbId;
+    ar >> m_iFileId;
     ar >> m_iSpecialSortSeason;
     ar >> m_iSpecialSortEpisode;
     ar >> m_iBookmarkId;
     ar >> m_iTrack;
+    ar >> m_streamDetails;
   }
 }
 
@@ -467,6 +505,48 @@
     node = node->NextSibling("artist");
   }
 
+  m_streamDetails.Reset();
+  node = movie->FirstChildElement("fileinfo");
+  if (node)
+  {
+    // Try to pull from fileinfo/streamdetails/[video|audio|subtitle] 
+    const TiXmlNode *nodeStreamDetails = node->FirstChild("streamdetails");
+    if (nodeStreamDetails)
+    {
+      const TiXmlNode *nodeDetail = NULL;
+      while (nodeDetail = nodeStreamDetails->IterateChildren("audio", nodeDetail))
+      {
+        CStreamDetailAudio *p = new CStreamDetailAudio();
+        XMLUtils::GetString(nodeDetail, "codec", p->m_strCodec);
+        XMLUtils::GetString(nodeDetail, "language", p->m_strLanguage);
+        XMLUtils::GetInt(nodeDetail, "channels", p->m_iChannels);
+        p->m_strCodec.MakeLower();
+        p->m_strLanguage.MakeLower();
+        m_streamDetails.AddStream(p);
+      }
+      nodeDetail = NULL;
+      while (nodeDetail = nodeStreamDetails->IterateChildren("video", nodeDetail))
+      {
+        CStreamDetailVideo *p = new CStreamDetailVideo();
+        XMLUtils::GetString(nodeDetail, "codec", p->m_strCodec);
+        XMLUtils::GetFloat(nodeDetail, "aspect", p->m_fAspect);
+        XMLUtils::GetInt(nodeDetail, "width", p->m_iWidth);
+        XMLUtils::GetInt(nodeDetail, "height", p->m_iHeight);
+        p->m_strCodec.MakeLower();
+        m_streamDetails.AddStream(p);
+      }
+      nodeDetail = NULL;
+      while (nodeDetail = nodeStreamDetails->IterateChildren("subtitle", nodeDetail))
+      {
+        CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
+        XMLUtils::GetString(nodeDetail, "language", p->m_strLanguage);
+        p->m_strLanguage.MakeLower();
+        m_streamDetails.AddStream(p);
+      }
+    }
+    m_streamDetails.DetermineBestStreams();
+  }  /* if fileinfo */
+
   const TiXmlElement *epguide = movie->FirstChildElement("episodeguide");
   if (epguide)
   {
@@ -585,4 +665,8 @@
   }
 }
 
+bool CVideoInfoTag::HasStreamDetails() const
+{
+  return m_streamDetails.HasItems();
+}
 
Index: xbmc/VideoInfoTag.h
===================================================================
--- xbmc/VideoInfoTag.h	(revision 21009)
+++ xbmc/VideoInfoTag.h	(working copy)
@@ -25,6 +25,7 @@
 #include "utils/Archive.h"
 #include "utils/ScraperUrl.h"
 #include "utils/Fanart.h"
+#include "utils/StreamDetails.h"
 
 #include <vector>
 
@@ -44,6 +45,7 @@
   bool Save(TiXmlNode *node, const CStdString &tag, bool savePathInfo = true);
   virtual void Serialize(CArchive& ar);
   const CStdString GetCast(bool bIncludeRole = false) const;
+  bool HasStreamDetails() const;
 
   CStdString m_strDirector;
   CStdString m_strWritingCredits;
@@ -80,13 +82,16 @@
   int m_iYear;
   int m_iSeason;
   int m_iEpisode;
-  int m_iDbId;
+  int m_iDbId; 
+  long m_iFileId; 
   int m_iSpecialSortSeason;
   int m_iSpecialSortEpisode;
   int m_iTrack;
   float m_fRating;
   int m_iBookmarkId;
   CFanart m_fanart;
+  CStreamDetails m_streamDetails;
+
 private:
   void ParseNative(const TiXmlElement* movie);
   void ParseMyMovies(const TiXmlElement* movie);
