mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1233 lines
31 KiB
1233 lines
31 KiB
#include "pch.h"
|
|
#include "thisdll.h"
|
|
#include "ids.h"
|
|
#include "MediaProp.h"
|
|
|
|
|
|
#define MAX_DESCRIPTOR 256
|
|
|
|
#include <mmsystem.h>
|
|
#include <vfw.h>
|
|
#include <msacm.h>
|
|
|
|
|
|
// Wav file stuff
|
|
typedef struct
|
|
{
|
|
DWORD dwSize;
|
|
LONG nLength; // milliseconds
|
|
TCHAR szWaveFormat[ACMFORMATTAGDETAILS_FORMATTAG_CHARS];
|
|
PWAVEFORMATEX pwfx;
|
|
} WAVEDESC;
|
|
STDMETHODIMP GetWaveInfo(IN LPCTSTR pszFile, OUT WAVEDESC *p);
|
|
STDMETHODIMP GetWaveProperty(IN REFFMTID reffmtid, IN PROPID propid, IN const WAVEDESC* pWave, OUT PROPVARIANT* pVar);
|
|
|
|
|
|
const COLMAP* c_rgAVWavAudioProps[] =
|
|
{
|
|
{&g_CM_Duration},
|
|
{&g_CM_Bitrate},
|
|
{&g_CM_SampleRate},
|
|
{&g_CM_SampleSize},
|
|
{&g_CM_ChannelCount},
|
|
{&g_CM_Format},
|
|
};
|
|
|
|
const PROPSET_INFO g_rgAVWavPropStgs[] =
|
|
{
|
|
{ PSGUID_AUDIO, c_rgAVWavAudioProps, ARRAYSIZE(c_rgAVWavAudioProps)},
|
|
};
|
|
// Wav files
|
|
|
|
|
|
|
|
|
|
// Avi file stuff
|
|
typedef struct
|
|
{
|
|
DWORD dwSize;
|
|
LONG nLength; // milliseconds
|
|
LONG nWidth; // pixels
|
|
LONG nHeight; // pixels
|
|
LONG nBitDepth;
|
|
LONG cFrames;
|
|
LONG nFrameRate; // frames/1000 seconds
|
|
LONG nDataRate; // bytes/second
|
|
TCHAR szCompression[MAX_DESCRIPTOR];
|
|
TCHAR szStreamName[MAX_DESCRIPTOR];
|
|
TCHAR szWaveFormat[ACMFORMATTAGDETAILS_FORMATTAG_CHARS];
|
|
PWAVEFORMATEX pwfx;
|
|
} AVIDESC;
|
|
|
|
STDMETHODIMP GetAviInfo(IN LPCTSTR pszFile, OUT AVIDESC *p);
|
|
STDMETHODIMP GetAviProperty(IN REFFMTID reffmtid, IN PROPID propid, IN const AVIDESC* pAvi, OUT PROPVARIANT* pVar);
|
|
|
|
const COLMAP* c_rgAVAviAudioProps[] =
|
|
{
|
|
{&g_CM_Duration},
|
|
{&g_CM_SampleSize},
|
|
{&g_CM_Bitrate},
|
|
{&g_CM_Format},
|
|
};
|
|
|
|
const COLMAP* c_rgAVAviImageProps[] =
|
|
{
|
|
{&g_CM_Width},
|
|
{&g_CM_Height},
|
|
{&g_CM_Dimensions},
|
|
};
|
|
|
|
|
|
const COLMAP* c_rgAVAviVideoProps[] =
|
|
{
|
|
{&g_CM_FrameCount},
|
|
{&g_CM_FrameRate},
|
|
{&g_CM_Compression},
|
|
{&g_CM_BitrateV},
|
|
{&g_CM_SampleSizeV},
|
|
};
|
|
|
|
const COLMAP* c_rgAVAviSummaryProps[] =
|
|
{
|
|
{&g_CM_Title},
|
|
};
|
|
|
|
const PROPSET_INFO g_rgAVAviPropStgs[] =
|
|
{
|
|
{ PSGUID_AUDIO, c_rgAVAviAudioProps, ARRAYSIZE(c_rgAVAviAudioProps)},
|
|
{ PSGUID_SUMMARYINFORMATION, c_rgAVAviSummaryProps, ARRAYSIZE(c_rgAVAviSummaryProps)},
|
|
{ PSGUID_VIDEO, c_rgAVAviVideoProps, ARRAYSIZE(c_rgAVAviVideoProps)},
|
|
{ PSGUID_IMAGESUMMARYINFORMATION, c_rgAVAviImageProps, ARRAYSIZE(c_rgAVAviImageProps)},
|
|
};
|
|
// avi
|
|
|
|
|
|
|
|
|
|
// Midi file stuff
|
|
// Note: Midi files are REALLLLLY slow.
|
|
typedef struct
|
|
{
|
|
LONG nLength;
|
|
TCHAR szMidiCopyright[MAX_DESCRIPTOR];
|
|
TCHAR szMidiSequenceName[MAX_DESCRIPTOR];
|
|
} MIDIDESC;
|
|
|
|
STDMETHODIMP GetMidiInfo(IN LPCTSTR pszFile, OUT MIDIDESC *p);
|
|
STDMETHODIMP GetMidiProperty(IN REFFMTID reffmtid, IN PROPID propid, IN const MIDIDESC* pMidi, OUT PROPVARIANT* pVar);
|
|
|
|
const COLMAP* c_rgAVMidiAudioProps[] =
|
|
{
|
|
{&g_CM_Duration},
|
|
};
|
|
|
|
const COLMAP* c_rgAVMidiSummaryProps[] =
|
|
{
|
|
{&g_CM_Title}, // SequenceName
|
|
};
|
|
|
|
const PROPSET_INFO g_rgAVMidiPropStgs[] =
|
|
{
|
|
{ PSGUID_AUDIO, c_rgAVMidiAudioProps, ARRAYSIZE(c_rgAVMidiAudioProps)},
|
|
{ PSGUID_SUMMARYINFORMATION, c_rgAVMidiSummaryProps, ARRAYSIZE(c_rgAVMidiSummaryProps)},
|
|
};
|
|
// Midi
|
|
|
|
|
|
|
|
|
|
#define FOURCC_INFO mmioFOURCC('I','N','F','O')
|
|
#define FOURCC_DISP mmioFOURCC('D','I','S','P')
|
|
#define FOURCC_IARL mmioFOURCC('I','A','R','L')
|
|
#define FOURCC_IART mmioFOURCC('I','A','R','T')
|
|
#define FOURCC_ICMS mmioFOURCC('I','C','M','S')
|
|
#define FOURCC_ICMT mmioFOURCC('I','C','M','T')
|
|
#define FOURCC_ICOP mmioFOURCC('I','C','O','P')
|
|
#define FOURCC_ICRD mmioFOURCC('I','C','R','D')
|
|
#define FOURCC_ICRP mmioFOURCC('I','C','R','P')
|
|
#define FOURCC_IDIM mmioFOURCC('I','D','I','M')
|
|
#define FOURCC_IDPI mmioFOURCC('I','D','P','I')
|
|
#define FOURCC_IENG mmioFOURCC('I','E','N','G')
|
|
#define FOURCC_IGNR mmioFOURCC('I','G','N','R')
|
|
#define FOURCC_IKEY mmioFOURCC('I','K','E','Y')
|
|
#define FOURCC_ILGT mmioFOURCC('I','L','G','T')
|
|
#define FOURCC_IMED mmioFOURCC('I','M','E','D')
|
|
#define FOURCC_INAM mmioFOURCC('I','N','A','M')
|
|
#define FOURCC_IPLT mmioFOURCC('I','P','L','T')
|
|
#define FOURCC_IPRD mmioFOURCC('I','P','R','D')
|
|
#define FOURCC_ISBJ mmioFOURCC('I','S','B','J')
|
|
#define FOURCC_ISFT mmioFOURCC('I','S','F','T')
|
|
#define FOURCC_ISHP mmioFOURCC('I','S','H','P')
|
|
#define FOURCC_ISRC mmioFOURCC('I','S','R','C')
|
|
#define FOURCC_ISRF mmioFOURCC('I','S','R','F')
|
|
#define FOURCC_ITCH mmioFOURCC('I','T','C','H')
|
|
#define FOURCC_VIDC mmioFOURCC('V','I','D','C')
|
|
#define mmioWAVE mmioFOURCC('W','A','V','E')
|
|
#define mmioFMT mmioFOURCC('f','m','t',' ')
|
|
#define mmioDATA mmioFOURCC('d','a','t','a')
|
|
#define MAXNUMSTREAMS 50
|
|
|
|
|
|
|
|
//#define _MIDI_PROPERTY_SUPPORT_
|
|
|
|
STDMETHODIMP GetMidiInfo(LPCTSTR pszFile, MIDIDESC *pmidi)
|
|
{
|
|
|
|
#ifdef _MIDI_PROPERTY_SUPPORT_
|
|
MCI_OPEN_PARMS mciOpen; /* Structure for MCI_OPEN command */
|
|
DWORD dwFlags;
|
|
DWORD dw;
|
|
MCIDEVICEID wDevID;
|
|
MCI_STATUS_PARMS mciStatus;
|
|
MCI_SET_PARMS mciSet; /* Structure for MCI_SET command */
|
|
MCI_INFO_PARMS mciInfo;
|
|
/* Open a file with an explicitly specified device */
|
|
|
|
mciOpen.lpstrDeviceType = TEXT("sequencer");
|
|
mciOpen.lpstrElementName = pszFile;
|
|
dwFlags = MCI_WAIT | MCI_OPEN_ELEMENT | MCI_OPEN_TYPE;
|
|
dw = mciSendCommand((MCIDEVICEID)0, MCI_OPEN, dwFlags,(DWORD_PTR)(LPVOID)&mciOpen);
|
|
if (dw)
|
|
return E_FAIL;
|
|
wDevID = mciOpen.wDeviceID;
|
|
|
|
mciSet.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
|
|
|
|
dw = mciSendCommand(wDevID, MCI_SET, MCI_SET_TIME_FORMAT,
|
|
(DWORD_PTR) (LPVOID) &mciSet);
|
|
if (dw)
|
|
{
|
|
mciSendCommand(wDevID, MCI_CLOSE, 0L, (DWORD)0);
|
|
return E_FAIL;
|
|
}
|
|
|
|
mciStatus.dwItem = MCI_STATUS_LENGTH;
|
|
dw = mciSendCommand(wDevID, MCI_STATUS, MCI_STATUS_ITEM,
|
|
(DWORD_PTR) (LPTSTR) &mciStatus);
|
|
if (dw)
|
|
pmidi->nLength = 0;
|
|
else
|
|
pmidi->nLength = (UINT)mciStatus.dwReturn;
|
|
|
|
mciInfo.dwCallback = 0;
|
|
|
|
mciInfo.lpstrReturn = pmidi->szMidiCopyright;
|
|
mciInfo.dwRetSize = sizeof(pmidi->szMidiCopyright);
|
|
*mciInfo.lpstrReturn = 0;
|
|
mciSendCommand(wDevID, MCI_INFO, MCI_INFO_COPYRIGHT, (DWORD_PTR)(LPVOID)&mciInfo);
|
|
|
|
mciInfo.lpstrReturn = pmidi->szMidiCopyright;
|
|
mciInfo.dwRetSize = sizeof(pmidi->szMidiSequenceName);
|
|
*mciInfo.lpstrReturn = 0;
|
|
mciSendCommand(wDevID, MCI_INFO, MCI_INFO_NAME, (DWORD_PTR)(LPVOID)&mciInfo);
|
|
|
|
mciSendCommand(wDevID, MCI_CLOSE, 0L, (DWORD)0);
|
|
|
|
return S_OK;
|
|
|
|
#else _MIDI_PROPERTY_SUPPORT_
|
|
|
|
return E_FAIL;
|
|
|
|
#endif _MIDI_PROPERTY_SUPPORT_
|
|
}
|
|
|
|
STDMETHODIMP GetMidiProperty(
|
|
IN REFFMTID reffmtid,
|
|
IN PROPID pid,
|
|
IN const MIDIDESC* pMidi,
|
|
OUT PROPVARIANT* pVar)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (IsEqualGUID(reffmtid, FMTID_AudioSummaryInformation))
|
|
{
|
|
hr = S_OK;
|
|
switch (pid)
|
|
{
|
|
case PIDASI_TIMELENGTH:
|
|
if (0 >= pMidi->nLength)
|
|
return E_FAIL;
|
|
|
|
// This value is in milliseconds.
|
|
// However, we define duration to be in 100ns units, so multiply by 10000.
|
|
pVar->uhVal.LowPart = pMidi->nLength;
|
|
pVar->uhVal.HighPart = 0;
|
|
|
|
pVar->uhVal.QuadPart = pVar->uhVal.QuadPart * 10000;
|
|
pVar->vt = VT_UI8;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
static HRESULT ReadWaveHeader(HMMIO hmmio, WAVEDESC *pwd)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
MMCKINFO mmckRIFF;
|
|
MMCKINFO mmck;
|
|
WORD wFormatSize;
|
|
MMRESULT wError;
|
|
|
|
ZeroMemory(pwd, sizeof(*pwd));
|
|
|
|
mmckRIFF.fccType = mmioWAVE;
|
|
if ((wError = mmioDescend(hmmio, &mmckRIFF, NULL, MMIO_FINDRIFF)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
mmck.ckid = mmioFMT;
|
|
if ((wError = mmioDescend(hmmio, &mmck, &mmckRIFF, MMIO_FINDCHUNK)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (mmck.cksize < sizeof(WAVEFORMAT))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
wFormatSize = (WORD)mmck.cksize;
|
|
if (NULL != (pwd->pwfx = (PWAVEFORMATEX)new BYTE[wFormatSize]))
|
|
{
|
|
if ((DWORD)mmioRead(hmmio, (HPSTR)pwd->pwfx, mmck.cksize) != mmck.cksize)
|
|
{
|
|
goto retErr;
|
|
}
|
|
if (pwd->pwfx->wFormatTag == WAVE_FORMAT_PCM)
|
|
{
|
|
if (wFormatSize < sizeof(PCMWAVEFORMAT))
|
|
{
|
|
goto retErr;
|
|
}
|
|
}
|
|
else if ((wFormatSize < sizeof(WAVEFORMATEX)) ||
|
|
(wFormatSize < sizeof(WAVEFORMATEX) + pwd->pwfx->cbSize))
|
|
{
|
|
goto retErr;
|
|
}
|
|
if ((wError = mmioAscend(hmmio, &mmck, 0)))
|
|
{
|
|
goto retErr;
|
|
}
|
|
mmck.ckid = mmioDATA;
|
|
if ((wError = mmioDescend(hmmio, &mmck, &mmckRIFF, MMIO_FINDCHUNK)))
|
|
{
|
|
goto retErr;
|
|
}
|
|
pwd->dwSize = mmck.cksize;
|
|
return S_OK;
|
|
}
|
|
|
|
retErr:
|
|
delete [] (LPBYTE)pwd->pwfx;
|
|
return E_FAIL;
|
|
}
|
|
|
|
// Retrieves text representation of format tag
|
|
STDMETHODIMP GetWaveFormatTag(PWAVEFORMATEX pwfx, LPTSTR pszTag, IN ULONG cchTag)
|
|
{
|
|
ASSERT(pwfx);
|
|
ASSERT(pszTag);
|
|
ASSERT(cchTag);
|
|
|
|
ACMFORMATTAGDETAILS aftd;
|
|
ZeroMemory(&aftd, sizeof(aftd));
|
|
aftd.cbStruct = sizeof(ACMFORMATTAGDETAILSW);
|
|
aftd.dwFormatTag = pwfx->wFormatTag;
|
|
|
|
if (0 == acmFormatTagDetails(NULL, &aftd, ACM_FORMATTAGDETAILSF_FORMATTAG))
|
|
{
|
|
// copy to output.
|
|
lstrcpyn(pszTag, aftd.szFormatTag, cchTag);
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
STDMETHODIMP GetWaveInfo(IN LPCTSTR pszFile, OUT WAVEDESC *p)
|
|
{
|
|
HMMIO hmmio;
|
|
|
|
if (NULL == (hmmio = mmioOpen((LPTSTR)pszFile, NULL, MMIO_ALLOCBUF | MMIO_READ)))
|
|
return E_FAIL;
|
|
|
|
HRESULT hr = ReadWaveHeader(hmmio, p);
|
|
|
|
mmioClose(hmmio, 0);
|
|
|
|
if (SUCCEEDED(hr) && p->pwfx)
|
|
{
|
|
// Retrieve text representation of format tag
|
|
GetWaveFormatTag(p->pwfx, p->szWaveFormat, ARRAYSIZE(p->szWaveFormat));
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP FreeWaveInfo(IN OUT WAVEDESC *p)
|
|
{
|
|
if (!(p && sizeof(*p) == p->dwSize) && p->pwfx)
|
|
{
|
|
delete [] p->pwfx;
|
|
p->pwfx = NULL;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP _getWaveAudioProperty(
|
|
IN REFFMTID reffmtid,
|
|
IN PROPID pid,
|
|
IN const PWAVEFORMATEX pwfx,
|
|
OUT PROPVARIANT* pVar)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
TCHAR szBuf[MAX_DESCRIPTOR],
|
|
szFmt[MAX_DESCRIPTOR];
|
|
|
|
PropVariantInit(pVar);
|
|
*szBuf = *szFmt = 0;
|
|
|
|
if (pwfx && IsEqualGUID(reffmtid, FMTID_AudioSummaryInformation))
|
|
{
|
|
hr = S_OK;
|
|
switch (pid)
|
|
{
|
|
case PIDASI_AVG_DATA_RATE:
|
|
if (0 >= pwfx->nAvgBytesPerSec)
|
|
return E_FAIL;
|
|
|
|
// Convert into bits per sec.
|
|
pVar->ulVal = pwfx->nAvgBytesPerSec * 8;
|
|
pVar->vt = VT_UI4;
|
|
break;
|
|
|
|
case PIDASI_SAMPLE_RATE:
|
|
if (0 >= pwfx->nSamplesPerSec)
|
|
return E_FAIL;
|
|
|
|
// Samples per second (/1000 to get kHz)
|
|
pVar->ulVal = pwfx->nSamplesPerSec;
|
|
pVar->vt = VT_UI4;
|
|
break;
|
|
|
|
case PIDASI_SAMPLE_SIZE:
|
|
if (0 >= pwfx->wBitsPerSample)
|
|
return E_FAIL;
|
|
|
|
// Bits per sample.
|
|
pVar->ulVal = pwfx->wBitsPerSample;
|
|
pVar->vt = VT_UI4;
|
|
break;
|
|
|
|
case PIDASI_CHANNEL_COUNT:
|
|
{
|
|
if (0 >= pwfx->nChannels)
|
|
return E_FAIL;
|
|
|
|
pVar->ulVal = pwfx->nChannels;
|
|
pVar->vt = VT_UI4;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return E_UNEXPECTED;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP GetWaveProperty(
|
|
IN REFFMTID reffmtid,
|
|
IN PROPID pid,
|
|
IN const WAVEDESC* pWave,
|
|
OUT PROPVARIANT* pVar)
|
|
{
|
|
|
|
HRESULT hr = E_FAIL;
|
|
TCHAR szBuf[MAX_DESCRIPTOR],
|
|
szFmt[MAX_DESCRIPTOR];
|
|
|
|
PropVariantInit(pVar);
|
|
*szBuf = *szFmt = 0;
|
|
|
|
if (IsEqualGUID(reffmtid, FMTID_AudioSummaryInformation))
|
|
{
|
|
hr = S_OK;
|
|
switch (pid)
|
|
{
|
|
case PIDASI_FORMAT:
|
|
if (0 == *pWave->szWaveFormat)
|
|
return E_FAIL;
|
|
|
|
hr = SHStrDupW(pWave->szWaveFormat, &pVar->pwszVal);
|
|
if (SUCCEEDED(hr))
|
|
pVar->vt = VT_LPWSTR;
|
|
else
|
|
return hr;
|
|
break;
|
|
|
|
// ISSUE: nLength is never filled in in GetWaveInfo, so this will always be zero.
|
|
case PIDASI_TIMELENGTH:
|
|
if (0 >= pWave->nLength)
|
|
return E_FAIL;
|
|
|
|
// This value is in milliseconds.
|
|
// However, we define duration to be in 100ns units, so multiply by 10000.
|
|
pVar->uhVal.LowPart = pWave->nLength;
|
|
pVar->uhVal.HighPart = 0;
|
|
|
|
pVar->uhVal.QuadPart = pVar->uhVal.QuadPart * 10000;
|
|
pVar->vt = VT_UI8;
|
|
break;
|
|
|
|
default:
|
|
hr = E_UNEXPECTED;
|
|
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
hr = _getWaveAudioProperty(reffmtid, pid, pWave->pwfx, pVar);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
STDMETHODIMP ReadAviStreams(LPCTSTR pszFile, DWORD dwFileSize, AVIDESC *pAvi)
|
|
{
|
|
HRESULT hr;
|
|
PAVIFILE pfile;
|
|
PAVISTREAM pavi;
|
|
PAVISTREAM rgpavis[MAXNUMSTREAMS]; // the current streams
|
|
AVISTREAMINFO avsi;
|
|
LONG timeStart; // cached start, end, length
|
|
LONG timeEnd;
|
|
int cpavi;
|
|
int i;
|
|
|
|
if (FAILED((hr = AVIFileOpen(&pfile, pszFile, 0, 0L))))
|
|
return hr;
|
|
|
|
for (i = 0; i <= MAXNUMSTREAMS; i++)
|
|
{
|
|
if (AVIFileGetStream(pfile, &pavi, 0L, i) != AVIERR_OK)
|
|
break;
|
|
if (i == MAXNUMSTREAMS)
|
|
{
|
|
AVIStreamRelease(pavi);
|
|
//DPF("Exceeded maximum number of streams");
|
|
break;
|
|
}
|
|
rgpavis[i] = pavi;
|
|
}
|
|
|
|
//
|
|
// Couldn't get any streams out of this file
|
|
//
|
|
if (i == 0)
|
|
{
|
|
//DPF("Unable to open any streams in %s", pszFile);
|
|
if (pfile)
|
|
AVIFileRelease(pfile);
|
|
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
|
|
cpavi = i;
|
|
|
|
//
|
|
// Start with bogus times
|
|
//
|
|
timeStart = 0x7FFFFFFF;
|
|
timeEnd = 0;
|
|
|
|
//
|
|
// Walk through and init all streams loaded
|
|
//
|
|
for (i = 0; i < cpavi; i++)
|
|
{
|
|
|
|
AVIStreamInfo(rgpavis[i], &avsi, sizeof(avsi));
|
|
|
|
switch (avsi.fccType)
|
|
{
|
|
case streamtypeVIDEO:
|
|
{
|
|
LONG cbFormat;
|
|
LPBYTE lpFormat;
|
|
ICINFO icInfo;
|
|
HIC hic;
|
|
DWORD dwTimeLen;
|
|
|
|
AVIStreamFormatSize(rgpavis[i], 0, &cbFormat);
|
|
dwTimeLen = AVIStreamEndTime(rgpavis[i]) - AVIStreamStartTime(rgpavis[i]);
|
|
pAvi->cFrames = avsi.dwLength;
|
|
pAvi->nFrameRate = MulDiv(avsi.dwLength, 1000000, dwTimeLen);
|
|
pAvi->nDataRate = MulDiv(dwFileSize, 1000000, dwTimeLen)/1024;
|
|
pAvi->nWidth = avsi.rcFrame.right - avsi.rcFrame.left;
|
|
pAvi->nHeight = avsi.rcFrame.bottom - avsi.rcFrame.top;
|
|
|
|
lstrcpyn(pAvi->szStreamName, avsi.szName, ARRAYSIZE(pAvi->szStreamName));
|
|
|
|
// Retrieve raster info (compression, bit depth).
|
|
lpFormat = new BYTE[cbFormat];
|
|
if (lpFormat)
|
|
{
|
|
AVIStreamReadFormat(rgpavis[i], 0, lpFormat, &cbFormat);
|
|
hic = (HIC)ICLocate(FOURCC_VIDC, avsi.fccHandler, (BITMAPINFOHEADER*)lpFormat,
|
|
NULL, (WORD)ICMODE_DECOMPRESS);
|
|
|
|
if (hic || ((LPBITMAPINFOHEADER)lpFormat)->biCompression == 0)
|
|
{
|
|
if (((LPBITMAPINFOHEADER)lpFormat)->biCompression)
|
|
{
|
|
ICGetInfo(hic, &icInfo, sizeof(ICINFO));
|
|
ICClose(hic);
|
|
lstrcpy(pAvi->szCompression, icInfo.szName);
|
|
}
|
|
else
|
|
{
|
|
LoadString(m_hInst, IDS_AVI_UNCOMPRESSED, pAvi->szCompression, ARRAYSIZE(pAvi->szCompression));
|
|
}
|
|
|
|
pAvi->nBitDepth = ((LPBITMAPINFOHEADER)lpFormat)->biBitCount;
|
|
}
|
|
delete [] lpFormat;
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
case streamtypeAUDIO:
|
|
{
|
|
LONG cbFormat;
|
|
|
|
AVIStreamFormatSize(rgpavis[i], 0, &cbFormat);
|
|
if ((pAvi->pwfx = (PWAVEFORMATEX) new BYTE[cbFormat]) != NULL)
|
|
{
|
|
ZeroMemory(pAvi->pwfx, cbFormat);
|
|
if (SUCCEEDED(AVIStreamReadFormat(rgpavis[i], 0, pAvi->pwfx, &cbFormat)))
|
|
GetWaveFormatTag(pAvi->pwfx, pAvi->szWaveFormat, ARRAYSIZE(pAvi->szWaveFormat));
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We're finding the earliest and latest start and end points for
|
|
// our scrollbar.
|
|
//
|
|
timeStart = min(timeStart, AVIStreamStartTime(rgpavis[i]));
|
|
timeEnd = max(timeEnd, AVIStreamEndTime(rgpavis[i]));
|
|
}
|
|
|
|
pAvi->nLength = (UINT)(timeEnd - timeStart);
|
|
|
|
for (i = 0; i < cpavi; i++)
|
|
{
|
|
AVIStreamRelease(rgpavis[i]);
|
|
}
|
|
AVIFileRelease(pfile);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// Because some AVI programs don't export the correct headers, retrieving AVI info will take
|
|
// an extremly long time on some files, we need to verify that the headers are available
|
|
// before we call AVIFileOpen
|
|
BOOL _ValidAviHeaderInfo(LPCTSTR pszFile)
|
|
{
|
|
BOOL fRet = FALSE; // Assume it is bad
|
|
|
|
HMMIO hmmio = mmioOpen((LPWSTR)pszFile, NULL, MMIO_READ);
|
|
if (hmmio)
|
|
{
|
|
MMCKINFO ckRIFF;
|
|
if (mmioDescend(hmmio, &ckRIFF, NULL, 0) == 0)
|
|
{
|
|
if ((ckRIFF.ckid == FOURCC_RIFF) && (ckRIFF.fccType == formtypeAVI))
|
|
{
|
|
MMCKINFO ckLIST;
|
|
ckLIST.fccType = listtypeAVIHEADER;
|
|
if (mmioDescend(hmmio, &ckLIST, &ckRIFF, MMIO_FINDLIST) == 0)
|
|
{
|
|
ckRIFF.ckid = ckidAVIMAINHDR;
|
|
if (mmioDescend(hmmio, &ckRIFF, &ckLIST, MMIO_FINDCHUNK) == 0)
|
|
{
|
|
MainAVIHeader Hdr;
|
|
ULONG cb = min(sizeof Hdr, ckRIFF.cksize);
|
|
|
|
if (mmioRead(hmmio, (HPSTR)&Hdr, cb) == cb)
|
|
{
|
|
fRet = Hdr.dwFlags & AVIF_HASINDEX;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
mmioClose(hmmio, 0);
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
STDMETHODIMP GetAviInfo(LPCTSTR pszFile, AVIDESC *pavi)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
if (_ValidAviHeaderInfo(pszFile))
|
|
{
|
|
|
|
// Retrieve the file size
|
|
HANDLE hFile = CreateFile(pszFile,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (INVALID_HANDLE_VALUE == hFile)
|
|
{
|
|
DWORD dwRet = GetLastError();
|
|
return ERROR_SUCCESS != dwRet ? HRESULT_FROM_WIN32(dwRet) : E_UNEXPECTED;
|
|
}
|
|
|
|
DWORD dwFileSize = GetFileSize((HANDLE)hFile, NULL);
|
|
CloseHandle(hFile);
|
|
|
|
AVIFileInit();
|
|
hr = ReadAviStreams(pszFile, dwFileSize, pavi);
|
|
AVIFileExit();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP FreeAviInfo(IN OUT AVIDESC *p)
|
|
{
|
|
if (!(p && sizeof(*p) == p->dwSize) && p->pwfx)
|
|
{
|
|
delete [] p->pwfx;
|
|
p->pwfx = NULL;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP GetAviProperty(
|
|
IN REFFMTID reffmtid,
|
|
IN PROPID pid,
|
|
IN const AVIDESC* pAvi,
|
|
OUT PROPVARIANT* pVar)
|
|
{
|
|
|
|
HRESULT hr = E_UNEXPECTED;
|
|
TCHAR szBuf[MAX_DESCRIPTOR],
|
|
szFmt[MAX_DESCRIPTOR];
|
|
|
|
PropVariantInit(pVar);
|
|
*szBuf = *szFmt = 0;
|
|
|
|
if (IsEqualGUID(reffmtid, FMTID_SummaryInformation))
|
|
{
|
|
hr = S_OK;
|
|
switch (pid)
|
|
{
|
|
case PIDSI_TITLE:
|
|
if (0 == *pAvi->szStreamName)
|
|
return E_FAIL;
|
|
|
|
hr = SHStrDupW(pAvi->szStreamName, &pVar->pwszVal);
|
|
if (SUCCEEDED(hr))
|
|
pVar->vt = VT_LPWSTR;
|
|
else
|
|
return hr;
|
|
|
|
default:
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
}
|
|
else if (IsEqualGUID(reffmtid, FMTID_ImageSummaryInformation))
|
|
{
|
|
hr = S_OK;
|
|
switch (pid)
|
|
{
|
|
case PIDISI_CX:
|
|
if (0 >= pAvi->nWidth)
|
|
return E_FAIL;
|
|
|
|
pVar->ulVal = pAvi->nWidth;
|
|
pVar->vt = VT_UI4;
|
|
break;
|
|
|
|
case PIDISI_CY:
|
|
if (0 >= pAvi->nHeight)
|
|
return E_FAIL;
|
|
|
|
pVar->ulVal = pAvi->nHeight;
|
|
pVar->vt = VT_UI4;
|
|
break;
|
|
|
|
case PIDISI_FRAME_COUNT:
|
|
if (0 >= pAvi->cFrames)
|
|
return E_FAIL;
|
|
|
|
pVar->ulVal = pAvi->cFrames;
|
|
pVar->vt = VT_UI4;
|
|
break;
|
|
|
|
case PIDISI_DIMENSIONS:
|
|
if ((0 >= pAvi->nHeight) || (0 >= pAvi->nWidth))
|
|
return E_FAIL;
|
|
|
|
WCHAR szFmt[64];
|
|
if (LoadString(m_hInst, IDS_DIMENSIONS_FMT, szFmt, ARRAYSIZE(szFmt)))
|
|
{
|
|
DWORD_PTR args[2];
|
|
args[0] = (DWORD_PTR)pAvi->nWidth;
|
|
args[1] = (DWORD_PTR)pAvi->nHeight;
|
|
|
|
WCHAR szBuffer[64];
|
|
FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
szFmt, 0, 0, szBuffer, ARRAYSIZE(szBuffer), (va_list*)args);
|
|
|
|
hr = SHStrDup(szBuffer, &pVar->pwszVal);
|
|
if (SUCCEEDED(hr))
|
|
pVar->vt = VT_LPWSTR;
|
|
else
|
|
pVar->vt = VT_EMPTY;
|
|
}
|
|
else
|
|
hr = E_FAIL;
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
hr = E_UNEXPECTED;
|
|
|
|
}
|
|
}
|
|
else if (IsEqualGUID(reffmtid, FMTID_AudioSummaryInformation))
|
|
{
|
|
hr = S_OK;
|
|
switch (pid)
|
|
{
|
|
case PIDASI_TIMELENGTH:
|
|
if (0 >= pAvi->nLength)
|
|
return E_FAIL;
|
|
|
|
// This value is in milliseconds.
|
|
// However, we define duration to be in 100ns units, so multiply by 10000.
|
|
pVar->uhVal.LowPart = pAvi->nLength;
|
|
pVar->uhVal.HighPart = 0;
|
|
|
|
pVar->uhVal.QuadPart = pVar->uhVal.QuadPart * 10000;
|
|
pVar->vt = VT_UI8;
|
|
break;
|
|
|
|
case PIDASI_FORMAT:
|
|
if (0 == *pAvi->szWaveFormat)
|
|
return E_FAIL;
|
|
|
|
hr = SHStrDupW(pAvi->szWaveFormat, &pVar->pwszVal);
|
|
if (SUCCEEDED(hr))
|
|
pVar->vt = VT_LPWSTR;
|
|
else
|
|
return hr;
|
|
break;
|
|
|
|
default:
|
|
hr = E_UNEXPECTED;
|
|
|
|
}
|
|
}
|
|
else if (IsEqualGUID(reffmtid, FMTID_VideoSummaryInformation))
|
|
{
|
|
hr = S_OK;
|
|
switch (pid)
|
|
{
|
|
|
|
case PIDVSI_FRAME_RATE:
|
|
if (0 >= pAvi->nFrameRate)
|
|
return E_FAIL;
|
|
|
|
// Value is in frames/millisecond.
|
|
pVar->ulVal = pAvi->nFrameRate;
|
|
pVar->vt = VT_UI4;
|
|
break;
|
|
|
|
case PIDVSI_DATA_RATE:
|
|
if (0 >= pAvi->nDataRate)
|
|
return E_FAIL;
|
|
|
|
// This is in bits or bytes per second.
|
|
pVar->ulVal = pAvi->nDataRate;
|
|
pVar->vt = VT_UI4;
|
|
break;
|
|
|
|
|
|
case PIDVSI_SAMPLE_SIZE:
|
|
if (0 >= pAvi->nBitDepth)
|
|
return E_FAIL;
|
|
|
|
// bit depth
|
|
pVar->ulVal = pAvi->nBitDepth;
|
|
pVar->vt = VT_UI4;
|
|
break;
|
|
|
|
case PIDVSI_COMPRESSION:
|
|
if (0 == *pAvi->szCompression)
|
|
return E_FAIL;
|
|
|
|
hr = SHStrDupW(pAvi->szCompression, &pVar->pwszVal);
|
|
if (SUCCEEDED(hr))
|
|
pVar->vt = VT_LPWSTR;
|
|
else
|
|
return hr;
|
|
break;
|
|
|
|
default:
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
hr = _getWaveAudioProperty(reffmtid, pid, pAvi->pwfx, pVar);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// declares
|
|
class CWavPropSetStg : public CMediaPropSetStg
|
|
{
|
|
public:
|
|
// IPersist
|
|
STDMETHODIMP GetClassID(CLSID *pClassID);
|
|
|
|
private:
|
|
HRESULT _PopulatePropertySet();
|
|
HRESULT _PopulateSlowProperties();
|
|
BOOL _IsSlowProperty(const COLMAP *pPInfo);
|
|
};
|
|
|
|
|
|
class CMidiPropSetStg : public CMediaPropSetStg
|
|
{
|
|
public:
|
|
// IPersist
|
|
STDMETHODIMP GetClassID(CLSID *pClassID);
|
|
|
|
private:
|
|
HRESULT _PopulatePropertySet();
|
|
};
|
|
|
|
|
|
class CAviPropSetStg : public CMediaPropSetStg
|
|
{
|
|
public:
|
|
// IPersist
|
|
STDMETHODIMP GetClassID(CLSID *pClassID);
|
|
|
|
private:
|
|
HRESULT _PopulatePropertySet();
|
|
HRESULT _PopulateSlowProperties();
|
|
BOOL _IsSlowProperty(const COLMAP *pPInfo);
|
|
};
|
|
|
|
|
|
|
|
//impls
|
|
|
|
// Wav property set storage
|
|
CWavPropSetStg::CWavPropSetStg() : CMediaPropSetStg()
|
|
{
|
|
_pPropStgInfo = g_rgAVWavPropStgs;
|
|
_cPropertyStorages = ARRAYSIZE(g_rgAVWavPropStgs);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CWavPropSetStg::GetClassID(CLSID *pClassID)
|
|
{
|
|
*pClassID = CLSID_AVWavProperties;
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL CWavPropSetStg::_IsSlowProperty(const COLMAP *pPInfo)
|
|
{
|
|
return TRUE; // It is slow to get WAV properties.
|
|
}
|
|
|
|
HRESULT CWavPropSetStg::_PopulateSlowProperties()
|
|
{
|
|
if (!_bSlowPropertiesExtracted)
|
|
{
|
|
_bSlowPropertiesExtracted = TRUE;
|
|
|
|
WAVEDESC wd = {0};
|
|
HRESULT hr = GetWaveInfo(_wszFile, &wd);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CEnumAllProps enumAllProps(_pPropStgInfo, _cPropertyStorages);
|
|
const COLMAP *pPInfo = enumAllProps.Next();
|
|
while (pPInfo)
|
|
{
|
|
PROPVARIANT var = {0};
|
|
if (SUCCEEDED(GetWaveProperty(pPInfo->pscid->fmtid,
|
|
pPInfo->pscid->pid,
|
|
&wd,
|
|
&var)))
|
|
{
|
|
_PopulateProperty(pPInfo, &var);
|
|
PropVariantClear(&var);
|
|
}
|
|
|
|
pPInfo = enumAllProps.Next();
|
|
}
|
|
}
|
|
|
|
_hrSlowProps = hr;
|
|
}
|
|
|
|
return _hrSlowProps;
|
|
}
|
|
|
|
|
|
HRESULT CWavPropSetStg::_PopulatePropertySet()
|
|
{
|
|
if (!_bHasBeenPopulated)
|
|
{
|
|
if (_wszFile[0] == 0)
|
|
{
|
|
_hrPopulated = STG_E_INVALIDNAME;
|
|
}
|
|
else
|
|
{
|
|
_hrPopulated = S_OK;
|
|
}
|
|
|
|
_bHasBeenPopulated = TRUE;
|
|
}
|
|
|
|
return _hrPopulated;
|
|
}
|
|
|
|
|
|
|
|
|
|
// midi property set storage
|
|
CMidiPropSetStg::CMidiPropSetStg() : CMediaPropSetStg()
|
|
{
|
|
_pPropStgInfo = g_rgAVMidiPropStgs;
|
|
_cPropertyStorages = ARRAYSIZE(g_rgAVMidiPropStgs);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CMidiPropSetStg::GetClassID(CLSID *pClassID)
|
|
{
|
|
*pClassID = CLSID_AVMidiProperties;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CMidiPropSetStg::_PopulatePropertySet()
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (_wszFile[0] == 0)
|
|
{
|
|
hr = STG_E_INVALIDNAME;
|
|
}
|
|
else if (_bHasBeenPopulated)
|
|
{
|
|
hr = _hrPopulated;
|
|
}
|
|
else
|
|
{
|
|
MIDIDESC md;
|
|
hr = GetMidiInfo(_wszFile, &md);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CEnumAllProps enumAllProps(_pPropStgInfo, _cPropertyStorages);
|
|
const COLMAP *pPInfo = enumAllProps.Next();
|
|
while (pPInfo)
|
|
{
|
|
PROPVARIANT var = {0};
|
|
if (SUCCEEDED(GetMidiProperty(pPInfo->pscid->fmtid,
|
|
pPInfo->pscid->pid,
|
|
&md,
|
|
&var)))
|
|
{
|
|
_PopulateProperty(pPInfo, &var);
|
|
}
|
|
|
|
pPInfo = enumAllProps.Next();
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// avi property set storage
|
|
CAviPropSetStg::CAviPropSetStg() : CMediaPropSetStg()
|
|
{
|
|
_pPropStgInfo = g_rgAVAviPropStgs;
|
|
_cPropertyStorages = ARRAYSIZE(g_rgAVAviPropStgs);
|
|
}
|
|
|
|
|
|
STDMETHODIMP CAviPropSetStg::GetClassID(CLSID *pClassID)
|
|
{
|
|
*pClassID = CLSID_AVAviProperties;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CAviPropSetStg::_PopulateSlowProperties()
|
|
{
|
|
if (!_bSlowPropertiesExtracted)
|
|
{
|
|
_bSlowPropertiesExtracted = TRUE;
|
|
|
|
|
|
AVIDESC ad = {0};
|
|
HRESULT hr = GetAviInfo(_wszFile, &ad);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CEnumAllProps enumAllProps(_pPropStgInfo, _cPropertyStorages);
|
|
const COLMAP *pPInfo = enumAllProps.Next();
|
|
while (pPInfo)
|
|
{
|
|
PROPVARIANT var = {0};
|
|
if (SUCCEEDED(GetAviProperty(pPInfo->pscid->fmtid,
|
|
pPInfo->pscid->pid,
|
|
&ad,
|
|
&var)))
|
|
{
|
|
_PopulateProperty(pPInfo, &var);
|
|
}
|
|
|
|
pPInfo = enumAllProps.Next();
|
|
}
|
|
}
|
|
|
|
_hrSlowProps = hr;
|
|
}
|
|
|
|
return _hrSlowProps;
|
|
}
|
|
|
|
|
|
BOOL CAviPropSetStg::_IsSlowProperty(const COLMAP *pPInfo)
|
|
{
|
|
return TRUE; // It is slow to get AVI properties.
|
|
}
|
|
|
|
HRESULT CAviPropSetStg::_PopulatePropertySet()
|
|
{
|
|
if (!_bHasBeenPopulated)
|
|
{
|
|
if (_wszFile[0] == 0)
|
|
{
|
|
_hrPopulated = STG_E_INVALIDNAME;
|
|
}
|
|
else
|
|
{
|
|
_hrPopulated = S_OK;
|
|
}
|
|
|
|
_bHasBeenPopulated = TRUE;
|
|
}
|
|
|
|
return _hrPopulated;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Creates
|
|
STDAPI CWavPropSetStg_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
|
|
{
|
|
HRESULT hr;
|
|
CWavPropSetStg *pPropSetStg = new CWavPropSetStg();
|
|
if (pPropSetStg)
|
|
{
|
|
hr = pPropSetStg->Init();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pPropSetStg->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
|
|
}
|
|
pPropSetStg->Release();
|
|
}
|
|
else
|
|
{
|
|
*ppunk = NULL;
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDAPI CMidiPropSetStg_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
|
|
{
|
|
HRESULT hr;
|
|
CMidiPropSetStg *pPropSetStg = new CMidiPropSetStg();
|
|
if (pPropSetStg)
|
|
{
|
|
hr = pPropSetStg->Init();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pPropSetStg->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
|
|
}
|
|
pPropSetStg->Release();
|
|
}
|
|
else
|
|
{
|
|
*ppunk = NULL;
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
STDAPI CAviPropSetStg_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
|
|
{
|
|
HRESULT hr;
|
|
CAviPropSetStg *pPropSetStg = new CAviPropSetStg();
|
|
if (pPropSetStg)
|
|
{
|
|
hr = pPropSetStg->Init();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pPropSetStg->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
|
|
}
|
|
pPropSetStg->Release();
|
|
}
|
|
else
|
|
{
|
|
*ppunk = NULL;
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|