Index: project/VS2008Express/XBMC.vcproj
===================================================================
--- project/VS2008Express/XBMC.vcproj	(revision 20932)
+++ project/VS2008Express/XBMC.vcproj	(working copy)
@@ -3022,6 +3022,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 20932)
+++ xbmc/Application.cpp	(working copy)
@@ -4418,6 +4418,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))
+          {
+            CVideoDatabase dbs;
+            dbs.Open();
+            dbs.SetStreamDetailsForFileId(details->m_streamDetails, details->m_iFileId);
+            dbs.Close();
+            CUtil::DeleteVideoDatabaseDirectoryCache();
+          }
+        }
+      }
     }
 #endif
 
Index: xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodec.h
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodec.h	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodec.h	(working copy)
@@ -84,9 +84,14 @@
   /*
    * returns the nr of channels for the decoded audio stream
    */
-  virtual int GetChannels() = 0;
+  virtual int GetOutputChannels() = 0;
 
   /*
+   * returns the nr of channels in the source audio stream
+   */
+  virtual int GetSourceChannels() = 0;
+
+  /*
    * returns the samplerate for the decoded audio stream
    */
   virtual int GetSampleRate() = 0;
@@ -102,11 +107,18 @@
   virtual bool NeedPasstrough() { return false; }
 
   /*
-   * should return codecs name
+   * should return class name
    */
   virtual const char* GetName() = 0;
 
   /*
+   *
+   * should return actual underlying codec name in the case where a codec class is 
+   * representing several underlying codecs
+   */
+  virtual const char* GetCodecName() = 0;
+
+  /*
    * should return amount of data decoded has buffered in preparation for next audio frame
    */
   virtual int GetBufferSize() { return 0; }
Index: xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.cpp	(working copy)
@@ -212,7 +212,7 @@
   m_iBuffered = 0;
 }
 
-int CDVDAudioCodecFFmpeg::GetChannels()
+int CDVDAudioCodecFFmpeg::GetSourceChannels()
 {
   if (m_pCodecContext) return m_pCodecContext->channels;
   return 0;
@@ -228,3 +228,13 @@
 {
   return 16;
 }
+
+const char* CDVDAudioCodecFFmpeg::GetCodecName()
+{
+  if (m_pCodecContext && m_pCodecContext->codec)
+  {
+    return m_pCodecContext->codec->name;
+  }
+  else 
+    return NULL;
+}
Index: xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.h
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.h	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecFFmpeg.h	(working copy)
@@ -35,10 +35,12 @@
   virtual int Decode(BYTE* pData, int iSize);
   virtual int GetData(BYTE** dst);
   virtual void Reset();
-  virtual int GetChannels();
+  virtual int GetOutputChannels() { return GetSourceChannels(); }
+  virtual int GetSourceChannels();
   virtual int GetSampleRate();
   virtual int GetBitsPerSample();
   virtual const char* GetName() { return "FFmpeg"; }
+  virtual const char* GetCodecName();
   virtual int GetBufferSize() { return m_iBuffered; }
 
 protected:
@@ -60,3 +62,4 @@
 
 };
 
+
Index: xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLiba52.h
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLiba52.h	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLiba52.h	(working copy)
@@ -40,11 +40,13 @@
   virtual int Decode(BYTE* pData, int iSize);
   virtual int GetData(BYTE** dst);
   virtual void Reset();
-  virtual int GetChannels()      { return m_iOutputChannels; }
+  virtual int GetOutputChannels() { return m_iOutputChannels; }
+  virtual int GetSourceChannels() { return m_iSourceChannels; }
   virtual int GetSampleRate()    { return m_iSourceSampleRate; }
   virtual int GetBufferSize()    { return m_inputSize; }
   virtual int GetBitsPerSample() { return 16; }
   virtual const char* GetName() { return "liba52"; }
+  virtual const char* GetCodecName() { return "ac3"; }
 
 protected:
   void SetDefault();
@@ -77,3 +79,4 @@
   int  m_inputSize;
 };
 
+
Index: xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibDts.h
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibDts.h	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibDts.h	(working copy)
@@ -34,11 +34,13 @@
   virtual int Decode(BYTE* pData, int iSize);
   virtual int GetData(BYTE** dst);
   virtual void Reset();
-  virtual int GetChannels()      { return m_iOutputChannels; }
+  virtual int GetOutputChannels() { return m_iOutputChannels; }
+  virtual int GetSourceChannels() { return m_iSourceChannels; }
   virtual int GetSampleRate()    { return m_iSourceSampleRate; }
   virtual int GetBufferSize()    { return m_inputSize; }
   virtual int GetBitsPerSample() { return 16; }
   virtual const char* GetName()  { return "libdts"; }
+  virtual const char* GetCodecName() { return "dts"; }
 
 protected:
   void SetDefault();
Index: xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibFaad.h
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibFaad.h	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibFaad.h	(working copy)
@@ -37,10 +37,12 @@
   virtual int Decode(BYTE* pData, int iSize);
   virtual int GetData(BYTE** dst);
   virtual void Reset();
-  virtual int GetChannels()      { return m_iSourceChannels; }
+  virtual int GetOutputChannels() { return GetSourceChannels(); }
+  virtual int GetSourceChannels() { return m_iSourceChannels; }
   virtual int GetSampleRate()    { return m_iSourceSampleRate; }
   virtual int GetBitsPerSample() { return 16; }
   virtual const char* GetName()  { return "libfaad"; }
+  virtual const char* GetCodecName() { return "aac"; }
   virtual int GetBufferSize()    { return m_InputBufferSize; }
 
 private:
Index: xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibMad.cpp
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibMad.cpp	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibMad.cpp	(working copy)
@@ -153,6 +153,7 @@
         m_iSourceSampleRate = m_frame.header.samplerate;
 	      m_iSourceChannels = (m_frame.header.mode == MAD_MODE_SINGLE_CHANNEL) ? 1 : 2;
 	      m_iSourceBitrate = m_frame.header.bitrate;
+        m_iSourceLayer = m_frame.header.layer;
 
   /*
               switch (this->frame.header.layer) {
@@ -244,3 +245,18 @@
     m_iInputBufferSize = 0;
   }
 }
+
+const char* CDVDAudioCodecLibMad::GetCodecName()
+{
+  switch (m_iSourceLayer)
+  {
+    case MAD_LAYER_I: 
+      return "mp1";
+    case MAD_LAYER_II:
+      return "mp2";
+    case MAD_LAYER_III:
+      return "mp3";
+    default:
+      return "mpa";
+  }
+}
Index: xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibMad.h
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibMad.h	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibMad.h	(working copy)
@@ -37,10 +37,12 @@
   virtual int Decode(BYTE* pData, int iSize);
   virtual int GetData(BYTE** dst);
   virtual void Reset();
-  virtual int GetChannels()      { return m_iSourceChannels; }
+  virtual int GetOutputChannels() { return GetSourceChannels(); }
+  virtual int GetSourceChannels() { return m_iSourceChannels; }
   virtual int GetSampleRate()    { return m_iSourceSampleRate; }
   virtual int GetBitsPerSample() { return 16; }
   virtual const char* GetName()  { return "libmad"; }
+  virtual const char* GetCodecName();
   virtual int GetBufferSize()    { return m_iInputBufferSize; }
 
 private:
@@ -48,6 +50,7 @@
   int m_iSourceSampleRate;
   int m_iSourceChannels;
   int m_iSourceBitrate;
+  int m_iSourceLayer;
 
   bool m_bInitialized;
 
Index: xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLPcm.h
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLPcm.h	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLPcm.h	(working copy)
@@ -35,6 +35,7 @@
   virtual bool Open(CDVDStreamInfo &hints, CDVDCodecOptions &options);
   virtual int Decode(BYTE* pData, int iSize);
   virtual const char* GetName()  { return "lpcm"; }
+  virtual const char* GetCodecName() { return "lpcm"; }
 
 protected:
 
Index: xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthrough.cpp
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthrough.cpp	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthrough.cpp	(working copy)
@@ -369,7 +369,7 @@
   m_Synced = false;
 }
 
-int CDVDAudioCodecPassthrough::GetChannels()
+int CDVDAudioCodecPassthrough::GetOutputChannels()
 {
   //Can't return correct channels here as this is used to keep sync.
   //should probably have some other way to find out this
@@ -386,3 +386,12 @@
   return OUT_SAMPLESIZE;
 }
 
+const char* CDVDAudioCodecPassthrough::GetCodecName()
+{
+  if (m_Codec == CODEC_ID_AC3)
+    return "ac3";
+  if (m_Codec == CODEC_ID_DTS)
+    return "dts";
+  return NULL;
+}
+
Index: xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthrough.h
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthrough.h	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthrough.h	(working copy)
@@ -36,11 +36,13 @@
   virtual int Decode(BYTE* pData, int iSize);
   virtual int GetData(BYTE** dst);
   virtual void Reset();
-  virtual int GetChannels();
+  virtual int GetOutputChannels();
+  virtual int GetSourceChannels() { return 0; }
   virtual int GetSampleRate();
   virtual int GetBitsPerSample();
   virtual bool NeedPasstrough() { return true; }
   virtual const char* GetName()  { return "passthrough"; }
+  virtual const char* GetCodecName();  
 
 private:
   int ParseFrame(BYTE* data, int size, BYTE** frame, int* framesize);
Index: xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPcm.cpp
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPcm.cpp	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPcm.cpp	(working copy)
@@ -289,7 +289,7 @@
   //SetDefault();
 }
 
-int CDVDAudioCodecPcm::GetChannels()
+int CDVDAudioCodecPcm::GetOutputChannels()
 {
   return m_iOutputChannels;
 }
Index: xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPcm.h
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPcm.h	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPcm.h	(working copy)
@@ -33,10 +33,12 @@
   virtual int Decode(BYTE* pData, int iSize);
   virtual int GetData(BYTE** dst);
   virtual void Reset();
-  virtual int GetChannels();
+  virtual int GetOutputChannels();
+  virtual int GetSourceChannels() { return m_iSourceChannels; }
   virtual int GetSampleRate();
   virtual int GetBitsPerSample();
   virtual const char* GetName()  { return "pcm"; }
+  virtual const char* GetCodecName() { return "pcm"; }
 
 protected:
   virtual void SetDefault();
Index: xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h	(working copy)
@@ -136,7 +136,19 @@
 
   /*
    *
-   * should return codecs name
+   * should return class name
    */
   virtual const char* GetName() = 0;
+
+  /*
+   * should return actual underlying codec name in the case where a codec class is 
+   * representing several underlying codecs
+   */
+  virtual const char* GetCodecName() = 0;
+
+  /*
+   * should return actual underlying codec name in the case where a codec class is 
+   * representing several underlying codecs
+   */
+  virtual int GetPictureWidth() = 0;
 };
Index: xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.cpp	(working copy)
@@ -510,6 +510,23 @@
   }
 }
 
+const char* CDVDVideoCodecFFmpeg::GetCodecName()
+{
+  if (m_pCodecContext && m_pCodecContext->codec)
+  {
+    return m_pCodecContext->codec->name;
+  }
+  else 
+    return NULL;
+}
+
+int CDVDVideoCodecFFmpeg::GetPictureWidth()
+{
+  if (m_pCodecContext)
+    return m_pCodecContext->width;
+  return m_iPictureWidth;
+}
+
 #ifdef HAVE_LIBVDPAU
 CVDPAU* CDVDVideoCodecFFmpeg::GetContextVDPAU()
 {
Index: xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.h
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.h	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecFFmpeg.h	(working copy)
@@ -40,6 +40,8 @@
   virtual bool GetPicture(DVDVideoPicture* pDvdVideoPicture);
   virtual void SetDropState(bool bDrop);
   virtual const char* GetName() { return m_name.c_str(); }; // m_name is never changed after open
+  virtual const char* GetCodecName();
+  virtual int GetPictureWidth();
 
 #ifdef HAVE_LIBVDPAU
   CVDPAU* GetContextVDPAU();
@@ -67,3 +69,4 @@
   bool m_UsingSoftware;
 };
 
+
Index: xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecLibMpeg2.cpp
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecLibMpeg2.cpp	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecLibMpeg2.cpp	(working copy)
@@ -582,3 +582,10 @@
   //  simplify (pixel_width, pixel_height);
   return (height == 576) ? 1 : 2;
 }
+
+int CDVDVideoCodecLibMpeg2::GetPictureWidth()
+{
+  if (m_pCurrentBuffer)
+    return m_pCurrentBuffer->iWidth;
+  return 0;
+}
Index: xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecLibMpeg2.h
===================================================================
--- xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecLibMpeg2.h	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecLibMpeg2.h	(working copy)
@@ -38,6 +38,8 @@
 
   virtual void SetDropState(bool bDrop);
   virtual const char* GetName() { return "libmpeg2"; }
+  virtual const char* GetCodecName() { return "mpeg2"; }
+  virtual int GetPictureWidth();
 
 protected:
   DVDVideoPicture* GetBuffer(unsigned int width, unsigned int height);
Index: xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h
===================================================================
--- xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemux.h	(working copy)
@@ -294,4 +294,8 @@
    */
   CDemuxStreamSubtitle* GetStreamFromSubtitleId(int iSubtitleIndex);
 
+  /*
+   * convert a demux-specific codecid to something presentable to the user
+   */
+  virtual void CodecIDToName(CodecID idCodec, CStdString &strName) {};
 };
Index: xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp
===================================================================
--- xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.cpp	(working copy)
@@ -1113,3 +1113,13 @@
         return false;
     #endif
 }
+
+void CDVDDemuxFFmpeg::CodecIDToName(CodecID idCodec, CStdString &strName)
+{
+  if (m_dllAvCodec.IsLoaded())
+  {
+    AVCodec *codec = m_dllAvCodec.avcodec_find_decoder(idCodec);
+    if (codec)
+      strName = codec->name;
+  }
+}
Index: xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h
===================================================================
--- xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxFFmpeg.h	(working copy)
@@ -96,8 +96,8 @@
   int GetChapterCount();
   int GetChapter();
   void GetChapterName(std::string& strChapterName);
+  void CodecIDToName(CodecID idCodec, CStdString& strName);
 
-
   bool Aborted();
 
   AVFormatContext* m_pFormatContext;
Index: xbmc/cores/dvdplayer/DVDFileInfo.cpp
===================================================================
--- xbmc/cores/dvdplayer/DVDFileInfo.cpp	(revision 20932)
+++ 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,95 @@
 
 }
 
+/**
+ * \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;
+      float aspect = ((CDemuxStreamVideo *)stream)->fAspect;
+      if (aspect != 0.0f)
+        p->m_strAspect.Format("%.2f", aspect);  // how many digits should we store?
+      pDemux->CodecIDToName(stream->codec, 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->CodecIDToName(stream->codec, 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 20932)
+++ 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 20932)
+++ 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"
@@ -3025,6 +3027,38 @@
   return false;
 }
 
+int CDVDPlayer::GetChannels()
+{
+  int retVal = m_dvdPlayerAudio.GetChannels();
+  // If the audio player returns 0 channels, ask the demuxer
+  if (!retVal && m_pDemuxer && (m_CurrentAudio.id != -1))
+  {
+    CDemuxStreamAudio* stream = static_cast<CDemuxStreamAudio*>(m_pDemuxer->GetStream(m_CurrentAudio.id));
+    if (stream && (stream->iChannels > retVal))
+      retVal = stream->iChannels;
+  }
+
+  return retVal;
+}
+
+CStdString CDVDPlayer::GetAudioCodecName()
+{
+  return m_dvdPlayerAudio.GetCodecName();
+}
+
+CStdString CDVDPlayer::GetVideoCodecName()
+{
+  return m_dvdPlayerVideo.GetCodecName();
+}
+
+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 20932)
+++ 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() { return m_dvdPlayerVideo.GetPictureWidth(); }
+  virtual bool GetStreamDetails(CStreamDetails &details);
 
   virtual bool GetCurrentSubtitle(CStdString& strSubtitle);
 
@@ -363,3 +368,4 @@
   CPlayerOptions m_PlayerOptions;
 };
 
+
Index: xbmc/cores/dvdplayer/DVDPlayerAudio.cpp
===================================================================
--- xbmc/cores/dvdplayer/DVDPlayerAudio.cpp	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDPlayerAudio.cpp	(working copy)
@@ -259,7 +259,7 @@
   }
 
   /* update codec information from what codec gave ut */
-  m_streaminfo.channels = m_pAudioCodec->GetChannels();
+  m_streaminfo.channels = m_pAudioCodec->GetSourceChannels();
   m_streaminfo.samplerate = m_pAudioCodec->GetSampleRate();
 
   LeaveCriticalSection(&m_critCodecSection);
@@ -316,7 +316,7 @@
       // get decoded data and the size of it
       audioframe.size = m_pAudioCodec->GetData(&audioframe.data);
       audioframe.pts = m_audioClock;
-      audioframe.channels = m_pAudioCodec->GetChannels();
+      audioframe.channels = m_pAudioCodec->GetOutputChannels();
       audioframe.bits_per_sample = m_pAudioCodec->GetBitsPerSample();
       audioframe.sample_rate = m_pAudioCodec->GetSampleRate();
       audioframe.passthrough = m_pAudioCodec->NeedPasstrough();
@@ -766,3 +766,19 @@
 {
   return (int)m_audioStats.GetBitrate();
 }
+
+std::string CDVDPlayerAudio::GetCodecName()
+{
+  if (m_pAudioCodec)
+    return m_pAudioCodec->GetCodecName();
+  else
+    return "";
+}
+
+int CDVDPlayerAudio::GetChannels()
+{
+  if (m_pAudioCodec)
+    return m_pAudioCodec->GetSourceChannels();
+  else
+    return m_streaminfo.channels;
+}
Index: xbmc/cores/dvdplayer/DVDPlayerAudio.h
===================================================================
--- xbmc/cores/dvdplayer/DVDPlayerAudio.h	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDPlayerAudio.h	(working copy)
@@ -107,7 +107,9 @@
   void SetDynamicRangeCompression(long drc)             { m_dvdAudio.SetDynamicRangeCompression(drc); }
 
   std::string GetPlayerInfo();
+  std::string GetCodecName();
   int GetAudioBitrate();
+  int GetChannels();
 
   // holds stream information for current playing stream
   CDVDStreamInfo m_streaminfo;
Index: xbmc/cores/dvdplayer/DVDPlayerVideo.cpp
===================================================================
--- xbmc/cores/dvdplayer/DVDPlayerVideo.cpp	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDPlayerVideo.cpp	(working copy)
@@ -998,3 +998,16 @@
   return (int)m_videoStats.GetBitrate();
 }
 
+std::string CDVDPlayerVideo::GetCodecName()
+{
+  if (m_pVideoCodec)
+    return m_pVideoCodec->GetCodecName();
+  return "";
+}
+
+int CDVDPlayerVideo::GetPictureWidth()
+{
+  if (m_pVideoCodec)
+    return m_pVideoCodec->GetPictureWidth();
+  return 0;
+}
Index: xbmc/cores/dvdplayer/DVDPlayerVideo.h
===================================================================
--- xbmc/cores/dvdplayer/DVDPlayerVideo.h	(revision 20932)
+++ xbmc/cores/dvdplayer/DVDPlayerVideo.h	(working copy)
@@ -97,6 +97,8 @@
   double GetOutputDelay(); /* returns the expected delay, from that a packet is put in queue */
   std::string GetPlayerInfo();
   int GetVideoBitrate();
+  std::string GetCodecName();
+  int GetPictureWidth();
 
   void SetSpeed(int iSpeed);
 
@@ -176,3 +178,4 @@
   CRITICAL_SECTION m_critCodecSection;
 };
 
+
Index: xbmc/cores/IPlayer.h
===================================================================
--- xbmc/cores/IPlayer.h	(revision 20932)
+++ xbmc/cores/IPlayer.h	(working copy)
@@ -23,6 +23,7 @@
 
 #include "IAudioCallback.h"
 #include "Key.h"
+#include "utils/StreamDetails.h"
 
 class IPlayerCallback
 {
@@ -122,7 +123,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/DVDPlayerCodec.cpp
===================================================================
--- xbmc/cores/paplayer/DVDPlayerCodec.cpp	(revision 20932)
+++ xbmc/cores/paplayer/DVDPlayerCodec.cpp	(working copy)
@@ -151,7 +151,7 @@
     // We always ask ffmpeg to return s16le
     m_BitsPerSample = m_pAudioCodec->GetBitsPerSample();
     m_SampleRate = m_pAudioCodec->GetSampleRate();
-    m_Channels = m_pAudioCodec->GetChannels();
+    m_Channels = m_pAudioCodec->GetSourceChannels();
 
   }
 
Index: xbmc/cores/paplayer/PAPlayer.cpp
===================================================================
--- xbmc/cores/paplayer/PAPlayer.cpp	(revision 20932)
+++ 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 20932)
+++ 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);
@@ -207,3 +207,4 @@
 
 };
 
+
Index: xbmc/GUIWindowVideoBase.cpp
===================================================================
--- xbmc/GUIWindowVideoBase.cpp	(revision 20932)
+++ 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 20932)
+++ 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 20932)
+++ 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 20932)
+++ 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 20932)
+++ 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,11 @@
   return pItem->HasThumbnail();
 }
 
-CVideoThumbLoader::CVideoThumbLoader()
+CVideoThumbLoader::CVideoThumbLoader() : 
+  CThumbLoader(1), m_pStreamDetailsObs(NULL)
+  // ^^^ It is REQUIRED that only one background thread be created until the demuxers
+  // are threadsafe.  The CDVDDemuxFFmpeg::ff_avutil_log's buffer is not safe, and
+  // avcodec_open/close may not be compiled threadsafe.
 {
 }
 
@@ -82,7 +87,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 +101,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 +121,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 +139,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 +172,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 20932)
+++ 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 20932)
+++ xbmc/utils/GUIInfoManager.cpp	(working copy)
@@ -518,6 +518,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"))
   {
@@ -869,6 +874,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;
 }
@@ -1033,6 +1045,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:
@@ -2797,7 +2825,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;
@@ -3477,7 +3505,7 @@
   return item;
 }
 
-CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info ) const
+CStdString CGUIInfoManager::GetItemLabel(const CFileItem *item, int info) const
 {
   if (!item) return "";
 
@@ -3764,34 +3792,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 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);
 }
 
@@ -4002,6 +4072,20 @@
   m_currentFile->m_lStartOffset = 0;
 }
 
+CStdString CGUIInfoManager::VideoWidthToResolutionDescription(int iWidth) const
+{
+  if (iWidth == 0)
+    return "";
+
+  // Give HD resoultions 80 pixels of fudge so like 1264 width is still considered 720
+  if (iWidth < 1200)
+    return "480";
+  else if (iWidth < 1840)
+    return "720";
+  else 
+    return "1080";
+}
+
 const CFileItem& CGUIInfoManager::GetCurrentSlide() const
 {
   return *m_currentSlide;
Index: xbmc/utils/GUIInfoManager.h
===================================================================
--- xbmc/utils/GUIInfoManager.h	(revision 20932)
+++ xbmc/utils/GUIInfoManager.h	(working copy)
@@ -241,6 +241,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
@@ -464,6 +468,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)
@@ -615,6 +626,7 @@
   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;
 
   // 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 20932)
+++ 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), CStreamDetail(CStreamDetail::VIDEO)
+{
+}
+
+void CStreamDetailVideo::Serialize(CArchive& ar)
+{
+  CStreamDetail::Serialize(ar);
+  if (ar.IsStoring())
+  {
+    ar << m_strCodec;
+    ar << m_strAspect;
+    ar << m_iHeight;
+    ar << m_iWidth;
+  }
+  else
+  {
+    ar >>m_strCodec;
+    ar >> m_strAspect;
+    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 "";
+}
+
+CStdString CStreamDetails::GetVideoAspect(int idx) const
+{
+  CStreamDetailVideo *item = (CStreamDetailVideo *)GetNthStream(CStreamDetail::VIDEO, idx);
+  if (item)
+    return item->m_strAspect;
+  else
+    return "";
+}
+
+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;
+  CStdString m_strAspect;
+  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;
+  CStdString 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 20932)
+++ 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, strVideoAspect text, 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, strVideoAspect, iVideoWidth, iVideoHeight) "
+        "VALUES (%u,%i,'%s','%s',%i,%i)",
+        lFileId, (int)CStreamDetail::VIDEO,
+        details.GetVideoCodec(i).c_str(), details.GetVideoAspect(i).c_str(),
+        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_strAspect = pDS->fv(3).get_asString();
+        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, strVideoAspect text, 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);
@@ -4452,24 +4615,39 @@
 
     // get data from returned rows
     items.Reserve(iRowsFound);
+    DWORD times[6];
+    memset(times, 0, sizeof(times));
     while (!m_pDS->eof())
     {
-      long lMovieId = m_pDS->fv("idMovie").get_asLong();
+      LARGE_INTEGER t1,t2;
+      QueryPerformanceCounter(&t1);
       CVideoInfoTag movie = GetDetailsForMovie(m_pDS);
+      QueryPerformanceCounter(&t2); times[0] += t2.QuadPart - t1.QuadPart;
       if (g_settings.m_vecProfiles[0].getLockMode() == LOCK_MODE_EVERYONE ||
           g_passwordManager.bMasterUser                                   ||
           g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, g_settings.m_videoSources))
       {
+        QueryPerformanceCounter(&t1);
         CFileItemPtr pItem(new CFileItem(movie));
-        pItem->m_strPath.Format("%s%ld", strBaseDir.c_str(), lMovieId);
+        QueryPerformanceCounter(&t2); times[1] += t2.QuadPart - t1.QuadPart;
+        QueryPerformanceCounter(&t1);
+        pItem->m_strPath.Format("%s%ld", strBaseDir.c_str(), movie.m_iDbId);
+        QueryPerformanceCounter(&t2); times[2] += t2.QuadPart - t1.QuadPart;
+        QueryPerformanceCounter(&t1);
         pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED,movie.m_playCount > 0);
+        QueryPerformanceCounter(&t2); times[3] += t2.QuadPart - t1.QuadPart;
+        QueryPerformanceCounter(&t1);
         items.Add(pItem);
+        QueryPerformanceCounter(&t2); times[4] += t2.QuadPart - t1.QuadPart;
       }
+      QueryPerformanceCounter(&t1);
       m_pDS->next();
+      QueryPerformanceCounter(&t2); times[5] += t2.QuadPart - t1.QuadPart;
     }
 
-    CLog::Log(LOGDEBUG,"Time to retrieve movies from dataset = %d",
-              timeGetTime() - time);
+    CLog::Log(LOGDEBUG,"Time to retrieve %i movies from dataset = %d|%u,%u,%u,%u,%u,%u",
+              iRowsFound, timeGetTime() - time,
+              times[0],times[1],times[2],times[3],times[4],times[5]);
 
     // cleanup
     m_pDS->close();
@@ -6183,6 +6361,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());
Index: xbmc/VideoDatabase.h
===================================================================
--- xbmc/VideoDatabase.h	(revision 20932)
+++ 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 20932)
+++ 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=0; iStream<m_streamDetails.GetVideoStreamCount(); iStream++)
+    {
+      TiXmlElement stream("video");
+      XMLUtils::SetString(&stream, "codec", m_streamDetails.GetVideoCodec(iStream));
+      XMLUtils::SetString(&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=0; 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=0; 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 = p->m_strCodec.ToLower();
+        p->m_strLanguage = p->m_strLanguage.ToLower();
+        m_streamDetails.AddStream(p);
+      }
+      nodeDetail = NULL;
+      while (nodeDetail = nodeStreamDetails->IterateChildren("video", nodeDetail))
+      {
+        CStreamDetailVideo *p = new CStreamDetailVideo();
+        XMLUtils::GetString(nodeDetail, "codec", p->m_strCodec);
+        XMLUtils::GetString(nodeDetail, "aspect", p->m_strAspect);
+        XMLUtils::GetInt(nodeDetail, "width", p->m_iWidth);
+        XMLUtils::GetInt(nodeDetail, "height", p->m_iHeight);
+        p->m_strCodec = p->m_strCodec.ToLower();
+        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 = p->m_strLanguage.ToLower();
+        m_streamDetails.AddStream(p);
+      }
+    }
+    m_streamDetails.DetermineBestStreams();
+  }  /* if fileinfo */
+
   const TiXmlElement *epguide = movie->FirstChildElement("episodeguide");
   if (epguide)
   {
@@ -586,3 +666,7 @@
 }
 
 
+bool CVideoInfoTag::HasStreamDetails() const
+{
+  return m_streamDetails.HasItems();
+}
Index: xbmc/VideoInfoTag.h
===================================================================
--- xbmc/VideoInfoTag.h	(revision 20932)
+++ 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);
