Index: project/VS2008Express/XBMC.vcproj =================================================================== --- project/VS2008Express/XBMC.vcproj (revision 20962) +++ project/VS2008Express/XBMC.vcproj (working copy) @@ -3022,6 +3022,14 @@ > + + + + Index: xbmc/Application.cpp =================================================================== --- xbmc/Application.cpp (revision 20962) +++ 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) && details->HasStreamDetails()) + { + 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 20962) +++ 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 20962) +++ 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 20962) +++ 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,5 @@ }; + + Index: xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLiba52.h =================================================================== --- xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLiba52.h (revision 20962) +++ 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,5 @@ int m_inputSize; }; + + Index: xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibDts.h =================================================================== --- xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecLibDts.h (revision 20962) +++ 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 20962) +++ 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 20962) +++ 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 20962) +++ 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 20962) +++ 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 20962) +++ 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,13 @@ return OUT_SAMPLESIZE; } +const char* CDVDAudioCodecPassthrough::GetCodecName() +{ + if (m_Codec == CODEC_ID_AC3) + return "ac3"; + if (m_Codec == CODEC_ID_DTS) + return "dca"; + return NULL; +} + + Index: xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthrough.h =================================================================== --- xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthrough.h (revision 20962) +++ 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 20962) +++ 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 20962) +++ 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 20962) +++ 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 20962) +++ 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 20962) +++ 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,5 @@ bool m_UsingSoftware; }; + + Index: xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecLibMpeg2.cpp =================================================================== --- xbmc/cores/dvdplayer/DVDCodecs/Video/DVDVideoCodecLibMpeg2.cpp (revision 20962) +++ 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 20962) +++ 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 20962) +++ 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 20962) +++ 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 20962) +++ 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; @@ -134,3 +134,4 @@ CDVDInputStream* m_pInput; }; + Index: xbmc/cores/dvdplayer/DVDFileInfo.cpp =================================================================== --- xbmc/cores/dvdplayer/DVDFileInfo.cpp (revision 20962) +++ 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; iStreamGetNrOfStreams(); 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->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 20962) +++ 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 20962) +++ 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(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 20962) +++ 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,5 @@ CPlayerOptions m_PlayerOptions; }; + + Index: xbmc/cores/dvdplayer/DVDPlayerAudio.cpp =================================================================== --- xbmc/cores/dvdplayer/DVDPlayerAudio.cpp (revision 20962) +++ 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 20962) +++ 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; @@ -198,3 +200,4 @@ CRITICAL_SECTION m_critCodecSection; }; + Index: xbmc/cores/dvdplayer/DVDPlayerVideo.cpp =================================================================== --- xbmc/cores/dvdplayer/DVDPlayerVideo.cpp (revision 20962) +++ xbmc/cores/dvdplayer/DVDPlayerVideo.cpp (working copy) @@ -998,3 +998,17 @@ 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 20962) +++ 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,5 @@ CRITICAL_SECTION m_critCodecSection; }; + + Index: xbmc/cores/IPlayer.h =================================================================== --- xbmc/cores/IPlayer.h (revision 20962) +++ 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 20962) +++ 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 20962) +++ 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 20962) +++ 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,5 @@ }; + + Index: xbmc/GUIWindowVideoBase.cpp =================================================================== --- xbmc/GUIWindowVideoBase.cpp (revision 20962) +++ 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; @@ -1828,3 +1843,4 @@ } + Index: xbmc/GUIWindowVideoBase.h =================================================================== --- xbmc/GUIWindowVideoBase.h (revision 20962) +++ 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 20962) +++ 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 20962) +++ 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 20962) +++ 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() @@ -202,3 +225,4 @@ return true; } + Index: xbmc/ThumbLoader.h =================================================================== --- xbmc/ThumbLoader.h (revision 20962) +++ 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 20962) +++ 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 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); } @@ -4002,6 +4072,56 @@ 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"; +} + +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 20962) +++ 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,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. @@ -701,3 +714,4 @@ + Index: xbmc/utils/Makefile =================================================================== --- xbmc/utils/Makefile (revision 20962) +++ 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::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::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::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::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::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> (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::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 + +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 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 20962) +++ 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 &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 &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 &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 20962) +++ 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& vecDirectors, std::vector& vecGenres, std::vector& vecStudios); int GetPlayCount(long id); + void DeleteStreamDetails(long lFileId); CVideoInfoTag GetDetailsByTypeAndId(VIDEODB_CONTENT_TYPE type, long id); CVideoInfoTag GetDetailsForMovie(std::auto_ptr &pDS, bool needsCast = false); CVideoInfoTag GetDetailsForTvShow(std::auto_ptr &pDS, bool needsCast = false); Index: xbmc/VideoInfoTag.cpp =================================================================== --- xbmc/VideoInfoTag.cpp (revision 20962) +++ 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; iStreamInsertEndChild(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::GetFloat(nodeDetail, "aspect", p->m_fAspect); + 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,8 @@ } +bool CVideoInfoTag::HasStreamDetails() const +{ + return m_streamDetails.HasItems(); +} + Index: xbmc/VideoInfoTag.h =================================================================== --- xbmc/VideoInfoTag.h (revision 20962) +++ 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 @@ -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);