windows-nt/Source/XPSP1/NT/shell/ext/media/thumbnail.cpp
2020-09-26 16:20:57 +08:00

334 lines
8.7 KiB
C++

#include "pch.h"
#include "thisdll.h"
#include "wmwrap.h"
#include <streams.h>
#include <shlobj.h>
#include <QEdit.h>
class CVideoThumbnail : public IExtractImage,
public IPersistFile,
public IServiceProvider
{
public:
CVideoThumbnail();
STDMETHOD (QueryInterface)(REFIID riid, void **ppv);
STDMETHOD_(ULONG, AddRef) ();
STDMETHOD_(ULONG, Release) ();
// IExtractImage
STDMETHOD (GetLocation)(LPWSTR pszPathBuffer, DWORD cch,
DWORD * pdwPriority, const SIZE * prgSize,
DWORD dwRecClrDepth, DWORD *pdwFlags);
STDMETHOD (Extract)(HBITMAP *phBmpThumbnail);
// IPersistFile
STDMETHOD (GetClassID)(CLSID *pClassID);
STDMETHOD (IsDirty)();
STDMETHOD (Load)(LPCOLESTR pszFileName, DWORD dwMode);
STDMETHOD (Save)(LPCOLESTR pszFileName, BOOL fRemember);
STDMETHOD (SaveCompleted)(LPCOLESTR pszFileName);
STDMETHOD (GetCurFile)(LPOLESTR *ppszFileName);
// IServiceProvider
STDMETHOD (QueryService)(REFGUID guidService, REFIID riid, void **ppv);
private:
~CVideoThumbnail();
HRESULT _InitToVideoStream();
HRESULT _GetThumbnailBits(BITMAPINFO **ppbi);
LONG _cRef;
TCHAR _szPath[MAX_PATH];
IMediaDet *_pmedia;
SIZE _rgSize;
DWORD _dwRecClrDepth;
};
CVideoThumbnail::CVideoThumbnail() : _cRef(1)
{
DllAddRef();
}
CVideoThumbnail::~CVideoThumbnail()
{
if (_pmedia)
{
IUnknown_SetSite(_pmedia, NULL);
_pmedia->Release();
}
DllRelease();
}
HRESULT CVideoThumbnail::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
QITABENT(CVideoThumbnail, IExtractImage),
QITABENT(CVideoThumbnail, IPersistFile),
QITABENTMULTI(CVideoThumbnail, IPersist, IPersistFile),
QITABENT(CVideoThumbnail, IServiceProvider),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CVideoThumbnail::AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CVideoThumbnail::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
HRESULT CVideoThumbnail::_InitToVideoStream()
{
HRESULT hr = E_FAIL;
if (_pmedia)
{
hr = S_OK;
}
else
{
if (_szPath[0])
{
hr = CoCreateInstance(CLSID_MediaDet, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IMediaDet, &_pmedia));
if (SUCCEEDED(hr))
{
// set the site provider on the MediaDet object to
// allowed keyed apps to use ASF decoder
IUnknown_SetSite(_pmedia, SAFECAST(this, IServiceProvider*));
// really this takes a BSTR but since this is inproc this works
hr = _pmedia->put_Filename(_szPath);
if (SUCCEEDED(hr))
{
// now seek to the first video stream so we can get it's bits
long nStreams;
if (SUCCEEDED(_pmedia->get_OutputStreams(&nStreams)))
{
for (long i = 0; i < nStreams; i++)
{
_pmedia->put_CurrentStream(i);
GUID guid = {0};
_pmedia->get_StreamType(&guid);
if (guid == MEDIATYPE_Video)
break;
// else if (guid == MEDIATYPE_Audio)
// BOOL bHasAudio = TRUE;
}
}
}
}
}
}
return hr;
}
HRESULT CVideoThumbnail::_GetThumbnailBits(BITMAPINFO **ppbi)
{
*ppbi = NULL;
HRESULT hr = _InitToVideoStream();
if (SUCCEEDED(hr))
{
long iWidth = _rgSize.cx;
long iHeight = _rgSize.cy;
AM_MEDIA_TYPE mt;
hr = _pmedia->get_StreamMediaType(&mt);
if (SUCCEEDED(hr))
{
if (mt.formattype == FORMAT_VideoInfo)
{
VIDEOINFOHEADER * pvih = (VIDEOINFOHEADER *)mt.pbFormat;
iWidth = pvih->bmiHeader.biWidth;
iHeight = pvih->bmiHeader.biHeight;
}
/*
// REVIEW: Do we have any reason to support these additional types?
else if (mt.formattype == FORMAT_VideoInfo2 || mt.formattype == FORMAT_MPEGVideo)
{
// REVIEW: Does FORMAT_MPEGVideo really start with a VIDEOINFOHEADER2 structure?
VIDEOINFOHEADER2 * pvih = (VIDEOINFOHEADER2 *)mt.pbFormat;
iWidth = pvih->bmiHeader.biWidth;
iHeight = pvih->bmiHeader.biHeight;
}
*/
if (iWidth > _rgSize.cx || iHeight > _rgSize.cy)
{
if ( Int32x32To64(_rgSize.cx, iHeight) > Int32x32To64(iWidth,_rgSize.cy) )
{
// constrained by height
iWidth = MulDiv(iWidth, _rgSize.cy, iHeight);
if (iWidth < 1) iWidth = 1;
iHeight = _rgSize.cy;
}
else
{
// constrained by width
iHeight = MulDiv(iHeight, _rgSize.cx, iWidth);
if (iHeight < 1) iHeight = 1;
iWidth = _rgSize.cx;
}
}
CoTaskMemFree(mt.pbFormat);
if (mt.pUnk)
{
mt.pUnk->Release();
}
}
LONG lByteCount = 0;
hr = _pmedia->GetBitmapBits(0.0, &lByteCount, NULL, iWidth, iHeight);
if (SUCCEEDED(hr))
{
*ppbi = (BITMAPINFO *)LocalAlloc(LPTR, lByteCount);
if (*ppbi)
{
hr = _pmedia->GetBitmapBits(0.0, 0, (char *)*ppbi, iWidth, iHeight);
}
else
hr = E_OUTOFMEMORY;
}
}
return hr;
}
void *CalcBitsOffsetInDIB(LPBITMAPINFO pBMI)
{
int ncolors = pBMI->bmiHeader.biClrUsed;
if (ncolors == 0 && pBMI->bmiHeader.biBitCount <= 8)
ncolors = 1 << pBMI->bmiHeader.biBitCount;
if (pBMI->bmiHeader.biBitCount == 16 ||
pBMI->bmiHeader.biBitCount == 32)
{
if (pBMI->bmiHeader.biCompression == BI_BITFIELDS)
{
ncolors = 3;
}
}
return (void *) ((UCHAR *)&pBMI->bmiColors[0] + ncolors * sizeof(RGBQUAD));
}
STDMETHODIMP CVideoThumbnail::Extract(HBITMAP *phbmp)
{
*phbmp = NULL;
BITMAPINFO *pbi;
HRESULT hr = _GetThumbnailBits(&pbi);
if (SUCCEEDED(hr))
{
HDC hdc = GetDC(NULL);
if (hdc)
{
*phbmp = CreateDIBitmap(hdc, &pbi->bmiHeader, CBM_INIT, CalcBitsOffsetInDIB(pbi), pbi, DIB_RGB_COLORS);
ReleaseDC(NULL, hdc);
}
else
hr = E_FAIL;
LocalFree(pbi);
}
return hr;
}
STDMETHODIMP CVideoThumbnail::GetLocation(LPWSTR pszPath, DWORD cch, DWORD *pdwPrioirty, const SIZE *prgSize, DWORD dwRecClrDepth, DWORD *pdwFlags)
{
HRESULT hr = (*pdwFlags & IEIFLAG_ASYNC) ? E_PENDING : S_OK;
_rgSize = *prgSize;
_dwRecClrDepth = dwRecClrDepth;
*pdwFlags = IEIFLAG_CACHE;
StrCpyNW(pszPath, _szPath, cch);
return hr;
}
STDMETHODIMP CVideoThumbnail::GetClassID(CLSID *pClassID)
{
*pClassID = CLSID_VideoThumbnail;
return S_OK;
}
STDMETHODIMP CVideoThumbnail::IsDirty(void)
{
return S_OK; // no
}
STDMETHODIMP CVideoThumbnail::Load(LPCOLESTR pszFileName, DWORD dwMode)
{
lstrcpynW(_szPath, pszFileName, ARRAYSIZE(_szPath));
return S_OK;
}
STDMETHODIMP CVideoThumbnail::Save(LPCOLESTR pszFileName, BOOL fRemember)
{
return S_OK;
}
STDMETHODIMP CVideoThumbnail::SaveCompleted(LPCOLESTR pszFileName)
{
return S_OK;
}
STDMETHODIMP CVideoThumbnail::GetCurFile(LPOLESTR *ppszFileName)
{
return E_NOTIMPL;
}
// IServiceProvider
STDMETHODIMP CVideoThumbnail::QueryService(REFGUID guidService, REFIID riid, void **ppv)
{
// Return code for no service should be SVC_E_UNKNOWNSERVICE according to docs,
// but that does not exist. Return E_INVALIDARG instead.
HRESULT hr = E_INVALIDARG;
*ppv = NULL;
if (guidService == _uuidof(IWMReader))
{
IUnknown *punkCert;
hr = WMCreateCertificate(&punkCert);
if (SUCCEEDED(hr))
{
hr = punkCert->QueryInterface(riid, ppv);
punkCert->Release();
}
}
return hr;
}
STDAPI CVideoThumbnail_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
{
HRESULT hr;
CVideoThumbnail *pvt = new CVideoThumbnail();
if (pvt)
{
*ppunk = SAFECAST(pvt, IExtractImage *);
hr = S_OK;
}
else
{
*ppunk = NULL;
hr = E_OUTOFMEMORY;
}
return hr;
}