Source code of Windows XP (NT5)
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.

1902 lines
61 KiB

  1. #include "pch.h"
  2. #include "thisdll.h"
  3. #include "wmwrap.h"
  4. #include "MediaProp.h"
  5. #include <streams.h> // For VIDEOINFOHEADER, etc..
  6. #include <drmexternals.h>
  7. #include "ids.h"
  8. #define TRACK_ONE_BASED L"WM/TrackNumber"
  9. #define TRACK_ZERO_BASED L"WM/Track"
  10. // Struct used when collecting information about a file.
  11. // This is used when populating sl ow files, and the information within is retrieved by several
  12. // different methods.
  13. typedef struct
  14. {
  15. // DRM info
  16. LPWSTR pszLicenseInformation;
  17. DWORD dwPlayCount;
  18. FILETIME ftPlayStarts;
  19. FILETIME ftPlayExpires;
  20. // Audio properties
  21. LPWSTR pszStreamNameAudio;
  22. WORD wStreamNumberAudio;
  23. WORD nChannels;
  24. DWORD dwBitrateAudio;
  25. LPWSTR pszCompressionAudio;
  26. DWORD dwSampleRate;
  27. ULONG lSampleSizeAudio;
  28. // Video properties
  29. LPWSTR pszStreamNameVideo;
  30. WORD wStreamNumberVideo;
  31. WORD wBitDepth;
  32. DWORD dwBitrateVideo;
  33. LONG cx;
  34. LONG cy;
  35. LPWSTR pszCompressionVideo;
  36. DWORD dwFrames;
  37. DWORD dwFrameRate;
  38. } SHMEDIA_AUDIOVIDEOPROPS;
  39. // Helpers for putting information in SHMEDIA_AUDIOVIDEOPROPS
  40. void GetVideoProperties(IWMStreamConfig *pConfig, SHMEDIA_AUDIOVIDEOPROPS *pVideoProps);
  41. void GetVideoPropertiesFromHeader(VIDEOINFOHEADER *pvih, SHMEDIA_AUDIOVIDEOPROPS *pVideoProps);
  42. void GetVideoPropertiesFromBitmapHeader(BITMAPINFOHEADER *bmi, SHMEDIA_AUDIOVIDEOPROPS *pVideoProps);
  43. void InitializeAudioVideoProperties(SHMEDIA_AUDIOVIDEOPROPS *pAVProps);
  44. void FreeAudioVideoProperties(SHMEDIA_AUDIOVIDEOPROPS *pAVProps);
  45. void GetAudioProperties(IWMStreamConfig *pConfig, SHMEDIA_AUDIOVIDEOPROPS *pAudioProps);
  46. void AcquireLicenseInformation(IWMDRMReader *pReader, SHMEDIA_AUDIOVIDEOPROPS *pAVProps);
  47. HRESULT GetSlowProperty(REFFMTID fmtid, PROPID pid, SHMEDIA_AUDIOVIDEOPROPS *pAVProps, PROPVARIANT *pvar);
  48. void _AssertValidDRMStrings();
  49. // Window media audio supported formats
  50. // Applies to wma, mp3,...
  51. const COLMAP* c_rgWMADocSummaryProps[] =
  52. {
  53. {&g_CM_Category},
  54. };
  55. const COLMAP* c_rgWMASummaryProps[] =
  56. {
  57. {&g_CM_Author},
  58. {&g_CM_Title},
  59. {&g_CM_Comment},
  60. };
  61. const COLMAP* c_rgWMAMusicProps[] =
  62. {
  63. {&g_CM_Artist},
  64. {&g_CM_Album},
  65. {&g_CM_Year},
  66. {&g_CM_Track},
  67. {&g_CM_Genre},
  68. {&g_CM_Lyrics},
  69. };
  70. const COLMAP* c_rgWMADRMProps[] =
  71. {
  72. {&g_CM_Protected},
  73. {&g_CM_DRMDescription},
  74. {&g_CM_PlayCount},
  75. {&g_CM_PlayStarts},
  76. {&g_CM_PlayExpires},
  77. };
  78. const COLMAP* c_rgWMAAudioProps[] =
  79. {
  80. {&g_CM_Duration},
  81. {&g_CM_Bitrate},
  82. {&g_CM_ChannelCount},
  83. {&g_CM_SampleSize},
  84. {&g_CM_SampleRate},
  85. };
  86. const PROPSET_INFO g_rgWMAPropStgs[] =
  87. {
  88. { PSGUID_MUSIC, c_rgWMAMusicProps, ARRAYSIZE(c_rgWMAMusicProps) },
  89. { PSGUID_SUMMARYINFORMATION, c_rgWMASummaryProps, ARRAYSIZE(c_rgWMASummaryProps) },
  90. { PSGUID_DOCUMENTSUMMARYINFORMATION, c_rgWMADocSummaryProps, ARRAYSIZE(c_rgWMADocSummaryProps)},
  91. { PSGUID_AUDIO, c_rgWMAAudioProps, ARRAYSIZE(c_rgWMAAudioProps)},
  92. { PSGUID_DRM, c_rgWMADRMProps, ARRAYSIZE(c_rgWMADRMProps)},
  93. };
  94. // Windows media audio
  95. // Window media video supported formats
  96. // applies to wmv, asf, ...
  97. const COLMAP* c_rgWMVSummaryProps[] =
  98. {
  99. {&g_CM_Author},
  100. {&g_CM_Title},
  101. {&g_CM_Comment},
  102. };
  103. const COLMAP* c_rgWMVDRMProps[] =
  104. {
  105. {&g_CM_Protected},
  106. {&g_CM_DRMDescription},
  107. {&g_CM_PlayCount},
  108. {&g_CM_PlayStarts},
  109. {&g_CM_PlayExpires},
  110. };
  111. const COLMAP* c_rgWMVAudioProps[] =
  112. {
  113. {&g_CM_Duration},
  114. {&g_CM_Bitrate},
  115. {&g_CM_ChannelCount},
  116. {&g_CM_SampleSize},
  117. {&g_CM_SampleRate},
  118. };
  119. const COLMAP* c_rgWMVVideoProps[] =
  120. {
  121. {&g_CM_StreamName},
  122. {&g_CM_FrameRate},
  123. {&g_CM_SampleSizeV},
  124. {&g_CM_BitrateV},
  125. {&g_CM_Compression},
  126. };
  127. const COLMAP* c_rgWMVImageProps[] =
  128. {
  129. {&g_CM_Width},
  130. {&g_CM_Height},
  131. {&g_CM_Dimensions},
  132. {&g_CM_FrameCount},
  133. };
  134. const PROPSET_INFO g_rgWMVPropStgs[] =
  135. {
  136. { PSGUID_DRM, c_rgWMVDRMProps, ARRAYSIZE(c_rgWMVDRMProps) },
  137. { PSGUID_SUMMARYINFORMATION, c_rgWMVSummaryProps, ARRAYSIZE(c_rgWMVSummaryProps) },
  138. { PSGUID_AUDIO, c_rgWMVAudioProps, ARRAYSIZE(c_rgWMVAudioProps)},
  139. { PSGUID_VIDEO, c_rgWMVVideoProps, ARRAYSIZE(c_rgWMVVideoProps)},
  140. { PSGUID_IMAGESUMMARYINFORMATION, c_rgWMVImageProps, ARRAYSIZE(c_rgWMVImageProps)},
  141. };
  142. // Windows media video
  143. // Map from scids to corresponding WMSDK attributes, for some of the "fast" properties
  144. // retrieved via IWMHeaderInfo. Two of these properties may also be slow (if the values aren't available
  145. // via IWMHeaderInfo).
  146. typedef struct
  147. {
  148. const SHCOLUMNID *pscid;
  149. LPCWSTR pszSDKName;
  150. } SCIDTOSDK;
  151. const SCIDTOSDK g_rgSCIDToSDKName[] =
  152. {
  153. // SCID sdk name
  154. {&SCID_Author, L"Author"},
  155. {&SCID_Title, L"Title"},
  156. {&SCID_Comment, L"Description"},
  157. {&SCID_Category, L"WM/Genre"},
  158. {&SCID_MUSIC_Artist, L"Author"},
  159. {&SCID_MUSIC_Album, L"WM/AlbumTitle"},
  160. {&SCID_MUSIC_Year, L"WM/Year"},
  161. {&SCID_MUSIC_Genre, L"WM/Genre"},
  162. {&SCID_MUSIC_Track, NULL}, // Track is a special property, as evidenced by
  163. {&SCID_DRM_Protected, L"Is_Protected"}, // the fact that it doesn't have an SDK Name.
  164. {&SCID_AUDIO_Duration, L"Duration"}, // Duration is slow, but may also be fast, depending on the file
  165. {&SCID_AUDIO_Bitrate, L"Bitrate"}, // Bitrate is slow, but may also be fast, depending on the file
  166. {&SCID_MUSIC_Lyrics, L"WM/Lyrics"}, // Lyrics
  167. };
  168. // impl
  169. class CWMPropSetStg : public CMediaPropSetStg
  170. {
  171. public:
  172. HRESULT FlushChanges(REFFMTID fmtid, LONG cNumProps, const COLMAP **ppcmapInfo, PROPVARIANT *pVarProps, BOOL *pbDirtyFlags);
  173. BOOL _IsSlowProperty(const COLMAP *pPInfo);
  174. private:
  175. HRESULT _FlushProperty(IWMHeaderInfo *phi, const COLMAP *pPInfo, PROPVARIANT *pvar);
  176. HRESULT _PopulateSpecialProperty(IWMHeaderInfo *phi, const COLMAP *pPInfo);
  177. HRESULT _SetPropertyFromWMT(const COLMAP *pPInfo, WMT_ATTR_DATATYPE attrDatatype, UCHAR *pData, WORD cbSize);
  178. HRESULT _PopulatePropertySet();
  179. HRESULT _PopulateSlowProperties();
  180. HRESULT _GetSlowPropertyInfo(SHMEDIA_AUDIOVIDEOPROPS *pAVProps);
  181. LPCWSTR _GetSDKName(const COLMAP *pPInfo);
  182. BOOL _IsHeaderProperty(const COLMAP *pPInfo);
  183. HRESULT _OpenHeaderInfo(IWMHeaderInfo **pHeaderInfo, BOOL fReadingOnly);
  184. HRESULT _PreCheck();
  185. HRESULT _QuickLookup(const COLMAP *pPInfo, PROPVARIANT **ppvar);
  186. void _PostProcess();
  187. BOOL _fProtectedContent;
  188. BOOL _fDurationSlow;
  189. BOOL _fBitrateSlow;
  190. };
  191. #define HI_READONLY TRUE
  192. #define HI_READWRITE FALSE
  193. // The only difference between CWMA and CWMV is which properties they're initialized with.
  194. class CWMAPropSetStg : public CWMPropSetStg
  195. {
  196. public:
  197. CWMAPropSetStg() { _pPropStgInfo = g_rgWMAPropStgs; _cPropertyStorages = ARRAYSIZE(g_rgWMAPropStgs);};
  198. // IPersist
  199. STDMETHODIMP GetClassID(CLSID *pclsid) {*pclsid = CLSID_AudioMediaProperties; return S_OK;};
  200. };
  201. class CWMVPropSetStg : public CWMPropSetStg
  202. {
  203. public:
  204. CWMVPropSetStg() { _pPropStgInfo = g_rgWMVPropStgs; _cPropertyStorages = ARRAYSIZE(g_rgWMVPropStgs);};
  205. // IPersist
  206. STDMETHODIMP GetClassID(CLSID *pclsid) {*pclsid = CLSID_VideoMediaProperties; return S_OK;};
  207. };
  208. HRESULT CreateReader(REFIID riid, void **ppv)
  209. {
  210. IWMReader *pReader;
  211. HRESULT hr = WMCreateReader(NULL, 0, &pReader);
  212. if (SUCCEEDED(hr))
  213. {
  214. hr = pReader->QueryInterface(riid, ppv);
  215. pReader->Release();
  216. }
  217. return hr;
  218. }
  219. HRESULT CWMPropSetStg::_PopulateSlowProperties()
  220. {
  221. if (!_bSlowPropertiesExtracted)
  222. {
  223. _bSlowPropertiesExtracted = TRUE;
  224. SHMEDIA_AUDIOVIDEOPROPS avProps = {0};
  225. InitializeAudioVideoProperties(&avProps);
  226. HRESULT hr = _GetSlowPropertyInfo(&avProps);
  227. if (SUCCEEDED(hr))
  228. {
  229. // Iterate through all fmtid/pid pairs we want, and call GetSlowProperty
  230. CEnumAllProps enumAllProps(_pPropStgInfo, _cPropertyStorages);
  231. const COLMAP *pPInfo = enumAllProps.Next();
  232. while (pPInfo)
  233. {
  234. if (_IsSlowProperty(pPInfo))
  235. {
  236. PROPVARIANT var = {0};
  237. if (SUCCEEDED(GetSlowProperty(pPInfo->pscid->fmtid,
  238. pPInfo->pscid->pid,
  239. &avProps,
  240. &var)))
  241. {
  242. _PopulateProperty(pPInfo, &var);
  243. PropVariantClear(&var);
  244. }
  245. }
  246. pPInfo = enumAllProps.Next();
  247. }
  248. // Free info in structure
  249. FreeAudioVideoProperties(&avProps);
  250. hr = S_OK;
  251. }
  252. _hrSlowProps = hr;
  253. }
  254. return _hrSlowProps;
  255. }
  256. BOOL CWMPropSetStg::_IsSlowProperty(const COLMAP *pPInfo)
  257. {
  258. // Some properties can be slow or "fast", depending on the file.
  259. if (pPInfo == &g_CM_Bitrate)
  260. return _fBitrateSlow;
  261. if (pPInfo == &g_CM_Duration)
  262. return _fDurationSlow;
  263. // Other than that - if it had a name used for IWMHeaderInfo->GetAttributeXXX, then it's a fast property.
  264. for (int i = 0; i < ARRAYSIZE(g_rgSCIDToSDKName); i++)
  265. {
  266. if (IsEqualSCID(*pPInfo->pscid, *g_rgSCIDToSDKName[i].pscid))
  267. {
  268. // Definitely a fast property.
  269. return FALSE;
  270. }
  271. }
  272. // If it's not one of the IWMHeaderInfo properties, then it's definitely slow.
  273. return TRUE;
  274. }
  275. STDAPI_(BOOL) IsNullTime(const FILETIME *pft)
  276. {
  277. FILETIME ftNull = {0, 0};
  278. return CompareFileTime(&ftNull, pft) == 0;
  279. }
  280. HRESULT GetSlowProperty(REFFMTID fmtid, PROPID pid, SHMEDIA_AUDIOVIDEOPROPS *pAVProps, PROPVARIANT *pvar)
  281. {
  282. HRESULT hr = E_FAIL;
  283. if (IsEqualGUID(fmtid, FMTID_DRM))
  284. {
  285. switch (pid)
  286. {
  287. case PIDDRSI_PROTECTED:
  288. ASSERTMSG(FALSE, "WMPSS: Asking for PIDDRSI_PROTECTED as a slow property");
  289. break;
  290. case PIDDRSI_DESCRIPTION:
  291. if (pAVProps->pszLicenseInformation)
  292. {
  293. hr = SHStrDupW(pAVProps->pszLicenseInformation, &pvar->pwszVal);
  294. if (SUCCEEDED(hr))
  295. pvar->vt = VT_LPWSTR;
  296. }
  297. break;
  298. case PIDDRSI_PLAYCOUNT:
  299. if (pAVProps->dwPlayCount != -1)
  300. {
  301. pvar->vt = VT_UI4;
  302. pvar->ulVal = pAVProps->dwPlayCount;
  303. hr = S_OK;
  304. }
  305. break;
  306. case PIDDRSI_PLAYSTARTS:
  307. if (!IsNullTime(&pAVProps->ftPlayStarts))
  308. {
  309. pvar->vt = VT_FILETIME;
  310. pvar->filetime = pAVProps->ftPlayStarts;
  311. hr = S_OK;
  312. }
  313. break;
  314. case PIDDRSI_PLAYEXPIRES:
  315. if (!IsNullTime(&pAVProps->ftPlayExpires))
  316. {
  317. pvar->vt = VT_FILETIME;
  318. pvar->filetime = pAVProps->ftPlayExpires;
  319. hr = S_OK;
  320. }
  321. break;
  322. }
  323. }
  324. else if (IsEqualGUID(fmtid, FMTID_AudioSummaryInformation))
  325. {
  326. switch (pid)
  327. {
  328. // case PIDASI_FORMAT: Don't know how to get this yet.
  329. // case PIDASI_DURATION: Don't know how to get this yet, but it's usually available through IWMHeaderInfo
  330. case PIDASI_STREAM_NAME:
  331. if (pAVProps->pszStreamNameAudio != NULL)
  332. {
  333. hr = SHStrDupW(pAVProps->pszStreamNameAudio, &pvar->pwszVal);
  334. if (SUCCEEDED(hr))
  335. pvar->vt = VT_LPWSTR;
  336. }
  337. break;
  338. case PIDASI_STREAM_NUMBER:
  339. if (pAVProps->wStreamNumberAudio > 0)
  340. {
  341. pvar->vt = VT_UI2;
  342. pvar->uiVal = pAVProps->wStreamNumberAudio;
  343. hr = S_OK;
  344. }
  345. break;
  346. case PIDASI_AVG_DATA_RATE:
  347. if (pAVProps->dwBitrateAudio > 0)
  348. {
  349. pvar->vt = VT_UI4;
  350. pvar->ulVal = pAVProps->dwBitrateAudio;
  351. hr = S_OK;
  352. }
  353. break;
  354. case PIDASI_SAMPLE_RATE:
  355. if (pAVProps->dwSampleRate > 0)
  356. {
  357. pvar->vt = VT_UI4;
  358. pvar->ulVal = pAVProps->dwSampleRate;
  359. hr = S_OK;
  360. }
  361. break;
  362. case PIDASI_SAMPLE_SIZE:
  363. if (pAVProps->lSampleSizeAudio > 0)
  364. {
  365. pvar->vt = VT_UI4;
  366. pvar->ulVal = pAVProps->lSampleSizeAudio;
  367. hr = S_OK;
  368. }
  369. break;
  370. case PIDASI_CHANNEL_COUNT:
  371. if (pAVProps->nChannels > 0)
  372. {
  373. pvar->vt = VT_UI4;
  374. pvar->ulVal = pAVProps->nChannels;
  375. hr = S_OK;
  376. }
  377. break;
  378. // Not supported yet - don't know how to get this.
  379. case PIDASI_COMPRESSION:
  380. if (pAVProps->pszCompressionAudio != NULL)
  381. {
  382. hr = SHStrDupW(pAVProps->pszCompressionAudio, &pvar->pwszVal);
  383. if (SUCCEEDED(hr))
  384. pvar->vt = VT_LPWSTR;
  385. }
  386. break;
  387. }
  388. }
  389. else if (IsEqualGUID(fmtid, FMTID_VideoSummaryInformation))
  390. {
  391. switch (pid)
  392. {
  393. case PIDVSI_STREAM_NAME:
  394. if (pAVProps->pszStreamNameVideo != NULL)
  395. {
  396. hr = SHStrDupW(pAVProps->pszStreamNameVideo, &pvar->pwszVal);
  397. if (SUCCEEDED(hr))
  398. pvar->vt = VT_LPWSTR;
  399. }
  400. break;
  401. case PIDVSI_STREAM_NUMBER:
  402. if (pAVProps->wStreamNumberVideo > 0)
  403. {
  404. pvar->vt = VT_UI2;
  405. pvar->uiVal = pAVProps->wStreamNumberVideo;
  406. hr = S_OK;
  407. }
  408. break;
  409. // Not supported yet - don't know how to get this.
  410. case PIDVSI_FRAME_RATE:
  411. if (pAVProps->dwFrameRate > 0)
  412. {
  413. pvar->vt = VT_UI4;
  414. pvar->ulVal = pAVProps->dwFrameRate;
  415. hr = S_OK;
  416. }
  417. break;
  418. case PIDVSI_DATA_RATE:
  419. if (pAVProps->dwBitrateVideo > 0)
  420. {
  421. pvar->vt = VT_UI4;
  422. pvar->ulVal = pAVProps->dwBitrateVideo;
  423. hr = S_OK;
  424. }
  425. break;
  426. case PIDVSI_SAMPLE_SIZE:
  427. //This is bitdepth.
  428. if (pAVProps->wBitDepth > 0)
  429. {
  430. pvar->vt = VT_UI4;
  431. pvar->ulVal = (ULONG)pAVProps->wBitDepth;
  432. hr = S_OK;
  433. }
  434. break;
  435. // Not supported yet - don't know how to get this.
  436. case PIDVSI_COMPRESSION:
  437. if (pAVProps->pszCompressionVideo != NULL)
  438. {
  439. hr = SHStrDupW(pAVProps->pszCompressionVideo, &pvar->pwszVal);
  440. if (SUCCEEDED(hr))
  441. pvar->vt = VT_LPWSTR;
  442. }
  443. break;
  444. }
  445. }
  446. else if (IsEqualGUID(fmtid, FMTID_ImageSummaryInformation))
  447. {
  448. switch(pid)
  449. {
  450. case PIDISI_CX:
  451. if (pAVProps->cx > 0)
  452. {
  453. pvar->vt = VT_UI4;
  454. pvar->ulVal = pAVProps->cx;
  455. hr = S_OK;
  456. }
  457. break;
  458. case PIDISI_CY:
  459. if (pAVProps->cy > 0)
  460. {
  461. pvar->vt = VT_UI4;
  462. pvar->ulVal = pAVProps->cy;
  463. hr = S_OK;
  464. }
  465. break;
  466. case PIDISI_FRAME_COUNT:
  467. if (pAVProps->dwFrames > 0)
  468. {
  469. pvar->vt = VT_UI4;
  470. pvar->ulVal = pAVProps->dwFrames;
  471. hr = S_OK;
  472. }
  473. break;
  474. case PIDISI_DIMENSIONS:
  475. if ((pAVProps->cy > 0) && (pAVProps->cx > 0))
  476. {
  477. WCHAR szFmt[64];
  478. if (LoadString(m_hInst, IDS_DIMENSIONS_FMT, szFmt, ARRAYSIZE(szFmt)))
  479. {
  480. DWORD_PTR args[2];
  481. args[0] = (DWORD_PTR)pAVProps->cx;
  482. args[1] = (DWORD_PTR)pAVProps->cy;
  483. WCHAR szBuffer[64];
  484. FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY,
  485. szFmt, 0, 0, szBuffer, ARRAYSIZE(szBuffer), (va_list*)args);
  486. hr = SHStrDup(szBuffer, &pvar->pwszVal);
  487. if (SUCCEEDED(hr))
  488. pvar->vt = VT_LPWSTR;
  489. }
  490. }
  491. break;
  492. }
  493. }
  494. return hr;
  495. }
  496. typedef struct
  497. {
  498. UINT ridDays;
  499. UINT ridWeeks;
  500. UINT ridMonths;
  501. } TIMEDRMRIDS;
  502. // These are sentinel values.
  503. #define DRMRIDS_TYPE_NONE -1
  504. #define DRMRIDS_TYPE_NORIGHT -2
  505. // These are indices into the ridTimes array in the DRMRIDS structure.
  506. #define DRMRIDS_TYPE_BEFORE 0
  507. #define DRMRIDS_TYPE_NOTUNTIL 1
  508. #define DRMRIDS_TYPE_COUNTBEFORE 2
  509. #define DRMRIDS_TYPE_COUNTNOTUNTIL 3
  510. typedef struct
  511. {
  512. UINT ridNoRights;
  513. TIMEDRMRIDS ridTimes[4];
  514. UINT ridCountRemaining;
  515. } DRMRIDS;
  516. //*****************************************************************************
  517. // NOTE: wszCount parameter is optional... can populate just a date string.
  518. //*****************************************************************************
  519. HRESULT ChooseAndPopulateDateCountString(
  520. FILETIME ftCurrent, // current time
  521. FILETIME ftLicense, // license UTC time
  522. WCHAR *wszCount, // optional count string
  523. const TIMEDRMRIDS *pridTimes,
  524. WCHAR *wszOutValue, // returned formatted string
  525. DWORD cchOutValue ) // num chars in 'wszOutValue'
  526. {
  527. HRESULT hr = S_OK;
  528. // 'ftLicense' (the license time) is greater than the current time.
  529. // Determine how much greater, and use the appropriate string.
  530. ULARGE_INTEGER ulCurrent, ulLicense;
  531. WCHAR wszDiff[ 34 ];
  532. QWORD qwDiff;
  533. DWORD dwDiffDays;
  534. DWORD rid = 0;
  535. // Laborious conversion to I64 type.
  536. ulCurrent.LowPart = ftCurrent.dwLowDateTime;
  537. ulCurrent.HighPart = ftCurrent.dwHighDateTime;
  538. ulLicense.LowPart = ftLicense.dwLowDateTime;
  539. ulLicense.HighPart = ftLicense.dwHighDateTime;
  540. if ((QWORD)ulLicense.QuadPart > (QWORD)ulCurrent.QuadPart)
  541. qwDiff = (QWORD)ulLicense.QuadPart - (QWORD)ulCurrent.QuadPart;
  542. else
  543. qwDiff = (QWORD)ulCurrent.QuadPart - (QWORD)ulLicense.QuadPart;
  544. dwDiffDays = ( DWORD )( qwDiff / ( QWORD )864000000000); // number of 100-ns units in a day.
  545. // We'll count the partial day as 1, so increment.
  546. // NOTE: this means we will never show a string that says
  547. // "expires in 0 day(s)".
  548. dwDiffDays++;
  549. if ( 31 >= dwDiffDays )
  550. {
  551. rid = pridTimes->ridDays;
  552. }
  553. else if ( 61 >= dwDiffDays )
  554. {
  555. rid = pridTimes->ridWeeks;
  556. dwDiffDays /= 7; // derive # weeks
  557. }
  558. else
  559. {
  560. rid = pridTimes->ridMonths;
  561. dwDiffDays /= 30; // derive # months
  562. }
  563. _ltow(( long )dwDiffDays, wszDiff, 10 );
  564. WCHAR szDRMMsg[MAX_PATH];
  565. WCHAR* rgchArgList[2];
  566. rgchArgList[0] = wszDiff;
  567. rgchArgList[1] = wszCount; // may be NULL
  568. // Can't get FORMAT_MESSAGE_FROM_HMODULE to work with FormatMessage....
  569. LoadString(m_hInst, rid, szDRMMsg, ARRAYSIZE(szDRMMsg));
  570. FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, szDRMMsg, 0, 0, wszOutValue, cchOutValue, reinterpret_cast<char**>(rgchArgList));
  571. return( hr );
  572. }
  573. // Return S_FALSE indicates no information found in the state data struct.
  574. //*****************************************************************************
  575. HRESULT ParseDRMStateData(const WM_LICENSE_STATE_DATA *sdValue, // data from DRM
  576. const DRMRIDS *prids, // array of resource ID's
  577. WCHAR *wszOutValue, // ptr to output buffer
  578. DWORD cchOutValue, // number of chars in 'wszOutValue' buffer
  579. DWORD *pdwCount, // Extra non-string info: counts remaining.
  580. FILETIME *pftStarts, // Extra non string info: when it starts.
  581. FILETIME *pftExpires) // Extra non string info: when it expires.
  582. {
  583. HRESULT hr = S_OK;
  584. *pdwCount = -1;
  585. pftExpires->dwLowDateTime = 0;
  586. pftExpires->dwHighDateTime = 0;
  587. pftStarts->dwLowDateTime = 0;
  588. pftStarts->dwHighDateTime = 0;
  589. WCHAR wszCount[34];
  590. WCHAR wszTemp[MAX_PATH];
  591. DWORD dwNumCounts = sdValue->stateData[0].dwNumCounts;
  592. if (dwNumCounts != 0)
  593. {
  594. // We have a valid play count.
  595. ASSERTMSG(1 == dwNumCounts, "Invalid number of playcounts in DRM_LICENSE_STATE_DATA");
  596. (void)_ltow(( long )sdValue->stateData[ 0 ].dwCount[ 0 ], wszCount, 10 );
  597. // ** Bonus information to store off.
  598. *pdwCount = sdValue->stateData[0].dwCount[0];
  599. }
  600. // Now deal with dates.
  601. UINT dwNumDates = sdValue->stateData[ 0 ].dwNumDates;
  602. // Most licenses have at most one date... an expiration.
  603. // There should be at most 2 dates!!
  604. if (dwNumDates == 0)
  605. {
  606. // No dates.. if there is also no playcount, then it's unlimited play.
  607. if (*pdwCount == -1)
  608. {
  609. // We're done.
  610. hr = S_FALSE;
  611. }
  612. else
  613. {
  614. // No dates.. just a count. Fill it into proper string.
  615. LoadString(m_hInst, prids->ridCountRemaining, wszTemp, ARRAYSIZE(wszTemp));
  616. wnsprintf(wszOutValue, cchOutValue, wszTemp, wszCount);
  617. // We're done.
  618. }
  619. }
  620. else
  621. {
  622. DWORD dwCategory = sdValue->stateData[0].dwCategory;
  623. // There are dates.
  624. if (dwNumDates == 1)
  625. {
  626. // Is it start or end?
  627. if ((dwCategory == WM_DRM_LICENSE_STATE_FROM) || (dwCategory == WM_DRM_LICENSE_STATE_COUNT_FROM))
  628. {
  629. // Start.
  630. *pftStarts = sdValue->stateData[0].datetime[0];
  631. }
  632. else if ((dwCategory == WM_DRM_LICENSE_STATE_UNTIL) || (dwCategory == WM_DRM_LICENSE_STATE_COUNT_UNTIL))
  633. {
  634. // Expires.
  635. *pftExpires = sdValue->stateData[0].datetime[0];
  636. }
  637. else
  638. {
  639. ASSERTMSG(FALSE, "Unexpected dwCategory for 1 date in DRM_LICENSE_STATE_DATA");
  640. hr = E_FAIL;
  641. }
  642. }
  643. else if (dwNumDates == 2)
  644. {
  645. // A start and end date.
  646. ASSERTMSG((dwCategory == WM_DRM_LICENSE_STATE_FROM_UNTIL) || (dwCategory == WM_DRM_LICENSE_STATE_COUNT_FROM_UNTIL), "Unexpected dwCategory for 2 dates in DRM_LICENSE_STATE_DATA");
  647. *pftStarts = sdValue->stateData[0].datetime[0];
  648. *pftExpires = sdValue->stateData[0].datetime[1];
  649. }
  650. else
  651. {
  652. ASSERTMSG(FALSE, "Too many dates in DRM_LICENSE_STATE_DATA");
  653. hr = E_FAIL;
  654. }
  655. if (SUCCEEDED(hr))
  656. {
  657. // 7 cases here. * = license date. T = current time.
  658. // --------------------------
  659. // BEGIN | END
  660. // --------------------------
  661. // 1 T * "...not allowed for xxx"
  662. // 2 * T --- don't show anything - action is allowed ---
  663. // 3 T * "...expires in xxx"
  664. // 4 * T "...not allowed"
  665. // 5 T * * "...not allowed for xxx"
  666. // 6 * T * "...expires in xxx"
  667. // 7 * * T "...not allowed"
  668. DWORD dwType; // This can be an array index into prids->ridTimes[]
  669. FILETIME ftLicense;
  670. FILETIME ftCurrent;
  671. GetSystemTimeAsFileTime(&ftCurrent); // UTC time
  672. if (!IsNullTime(pftStarts))
  673. {
  674. // We have a start time.
  675. if (CompareFileTime(&ftCurrent, pftStarts) == -1)
  676. {
  677. // CASE 1,5. We're before the start time.
  678. dwType = (*pdwCount == -1) ? DRMRIDS_TYPE_NOTUNTIL : DRMRIDS_TYPE_COUNTNOTUNTIL;
  679. ftLicense = *pftStarts;
  680. }
  681. else
  682. {
  683. // We're after the start time
  684. if (!IsNullTime(pftExpires))
  685. {
  686. // We have an expire time, and we're after the start time.
  687. if (CompareFileTime(&ftCurrent, pftExpires) == -1)
  688. {
  689. // CASE 6. We're before the expire time. Use "expires in" strings.
  690. dwType = (*pdwCount == -1) ? DRMRIDS_TYPE_BEFORE : DRMRIDS_TYPE_COUNTBEFORE;
  691. ftLicense = *pftExpires;
  692. }
  693. else
  694. {
  695. // CASE 7. After the the expire time. Action is not allowed.
  696. dwType = DRMRIDS_TYPE_NORIGHT;
  697. }
  698. }
  699. else
  700. {
  701. // CASE 2. Nothing to show. Action is allowed, since we're after the start date, with no expiry.
  702. dwType = DRMRIDS_TYPE_NONE;
  703. }
  704. }
  705. }
  706. else
  707. {
  708. // No start time.
  709. ASSERT(!IsNullTime(pftExpires));
  710. // We have an expire time
  711. if (CompareFileTime(&ftCurrent, pftExpires) == -1)
  712. {
  713. // CASE 3. We're before the expire time. Use "expires in" strings.
  714. dwType = (*pdwCount == -1) ? DRMRIDS_TYPE_BEFORE : DRMRIDS_TYPE_COUNTBEFORE;
  715. ftLicense = *pftExpires;
  716. }
  717. else
  718. {
  719. // CASE 4. After the the expire time. Action is not allowed.
  720. dwType = DRMRIDS_TYPE_NORIGHT;
  721. }
  722. }
  723. if (dwType == DRMRIDS_TYPE_NORIGHT)
  724. {
  725. // Current time is >= 'ftLicense'. Just return the "no rights" string.
  726. LoadString(m_hInst, prids->ridNoRights, wszOutValue, cchOutValue );
  727. }
  728. else if (dwType != DRMRIDS_TYPE_NONE)
  729. {
  730. hr = ChooseAndPopulateDateCountString(
  731. ftCurrent,
  732. ftLicense,
  733. (*pdwCount != -1) ? wszCount : NULL,
  734. &prids->ridTimes[dwType],
  735. wszOutValue,
  736. cchOutValue);
  737. }
  738. else
  739. {
  740. // Nothing to display. Action is allowed.
  741. ASSERT(dwType == DRMRIDS_TYPE_NONE);
  742. }
  743. }
  744. }
  745. return hr;
  746. }
  747. void AppendLicenseInfo(SHMEDIA_AUDIOVIDEOPROPS *pAVProps, WCHAR *pszLicenseInfo)
  748. {
  749. WCHAR *pszLI = pAVProps->pszLicenseInformation;
  750. BOOL fFirstOne = (pszLI == NULL);
  751. // (fFirstOne ? 1 : 3) --> 2 extra char for '\r\n' (except first time), 1 extra char for terminating NULL
  752. pszLI = (WCHAR*)CoTaskMemRealloc(pszLI, (lstrlen(pszLicenseInfo) + lstrlen(pszLI) + (fFirstOne ? 1 : 3)) * sizeof(WCHAR));
  753. if (pszLI)
  754. {
  755. if (fFirstOne)
  756. {
  757. // Make sure we have something to StrCat to.
  758. pszLI[0] = 0;
  759. }
  760. else
  761. {
  762. StrCat(pszLI, L"\r\n");
  763. }
  764. StrCat(pszLI, pszLicenseInfo);
  765. pAVProps->pszLicenseInformation = pszLI; // in case it moved.
  766. }
  767. }
  768. const DRMRIDS g_drmridsPlay =
  769. {
  770. IDS_DRM_PLAYNORIGHTS,
  771. {
  772. {IDS_DRM_PLAYBEFOREDAYS, IDS_DRM_PLAYBEFOREWEEKS, IDS_DRM_PLAYBEFOREMONTHS},
  773. {IDS_DRM_PLAYNOTUNTILDAYS, IDS_DRM_PLAYNOTUNTILWEEKS, IDS_DRM_PLAYNOTUNTILMONTHS},
  774. {IDS_DRM_PLAYCOUNTBEFOREDAYS, IDS_DRM_PLAYCOUNTBEFOREWEEKS, IDS_DRM_PLAYCOUNTBEFOREMONTHS},
  775. {IDS_DRM_PLAYCOUNTNOTUNTILDAYS, IDS_DRM_PLAYCOUNTNOTUNTILWEEKS, IDS_DRM_PLAYCOUNTNOTUNTILMONTHS}
  776. },
  777. IDS_DRM_PLAYCOUNTREMAINING,
  778. };
  779. const DRMRIDS g_drmridsCopyToCD =
  780. {
  781. IDS_DRM_COPYCDNORIGHTS,
  782. {
  783. {IDS_DRM_COPYCDBEFOREDAYS, IDS_DRM_COPYCDBEFOREWEEKS, IDS_DRM_COPYCDBEFOREMONTHS},
  784. {IDS_DRM_COPYCDNOTUNTILDAYS, IDS_DRM_COPYCDNOTUNTILWEEKS, IDS_DRM_COPYCDNOTUNTILMONTHS},
  785. {IDS_DRM_COPYCDCOUNTBEFOREDAYS, IDS_DRM_COPYCDCOUNTBEFOREWEEKS, IDS_DRM_COPYCDCOUNTBEFOREMONTHS},
  786. {IDS_DRM_COPYCDCOUNTNOTUNTILDAYS, IDS_DRM_COPYCDCOUNTNOTUNTILWEEKS, IDS_DRM_COPYCDCOUNTNOTUNTILMONTHS}
  787. },
  788. IDS_DRM_COPYCDCOUNTREMAINING,
  789. };
  790. const DRMRIDS g_drmridsCopyToNonSDMIDevice =
  791. {
  792. IDS_DRM_COPYNONSDMINORIGHTS,
  793. {
  794. {IDS_DRM_COPYNONSDMIBEFOREDAYS, IDS_DRM_COPYNONSDMIBEFOREWEEKS, IDS_DRM_COPYNONSDMIBEFOREMONTHS},
  795. {IDS_DRM_COPYNONSDMINOTUNTILDAYS, IDS_DRM_COPYNONSDMINOTUNTILWEEKS, IDS_DRM_COPYNONSDMINOTUNTILMONTHS},
  796. {IDS_DRM_COPYNONSDMICOUNTBEFOREDAYS, IDS_DRM_COPYNONSDMICOUNTBEFOREWEEKS, IDS_DRM_COPYNONSDMICOUNTBEFOREMONTHS},
  797. {IDS_DRM_COPYNONSDMICOUNTNOTUNTILDAYS, IDS_DRM_COPYNONSDMICOUNTNOTUNTILWEEKS, IDS_DRM_COPYNONSDMICOUNTNOTUNTILMONTHS}
  798. },
  799. IDS_DRM_COPYNONSDMICOUNTREMAINING,
  800. };
  801. const DRMRIDS g_drmridsCopyToSDMIDevice =
  802. {
  803. IDS_DRM_COPYSDMINORIGHTS,
  804. {
  805. {IDS_DRM_COPYSDMIBEFOREDAYS, IDS_DRM_COPYSDMIBEFOREWEEKS, IDS_DRM_COPYSDMIBEFOREMONTHS},
  806. {IDS_DRM_COPYSDMINOTUNTILDAYS, IDS_DRM_COPYSDMINOTUNTILWEEKS, IDS_DRM_COPYSDMINOTUNTILMONTHS},
  807. {IDS_DRM_COPYSDMICOUNTBEFOREDAYS, IDS_DRM_COPYSDMICOUNTBEFOREWEEKS, IDS_DRM_COPYSDMICOUNTBEFOREMONTHS},
  808. {IDS_DRM_COPYSDMICOUNTNOTUNTILDAYS, IDS_DRM_COPYSDMICOUNTNOTUNTILWEEKS, IDS_DRM_COPYSDMICOUNTNOTUNTILMONTHS}
  809. },
  810. IDS_DRM_COPYSDMICOUNTREMAINING,
  811. };
  812. #define ACTIONALLOWED_PLAY L"ActionAllowed.Play"
  813. #define ACTIONALLOWED_COPYTOCD L"ActionAllowed.Print.redbook"
  814. #define ACTIONALLOWED_COPYTONONSMDI L"ActionAllowed.Transfer.NONSDMI"
  815. #define ACTIONALLOWED_COPYTOSMDI L"ActionAllowed.Transfer.SDMI"
  816. #define LICENSESTATE_PLAY L"LicenseStateData.Play"
  817. #define LICENSESTATE_COPYTOCD L"LicenseStateData.Print.redbook"
  818. #define LICENSESTATE_COPYTONONSMDI L"LicenseStateData.Transfer.NONSDMI"
  819. #define LICENSESTATE_COPYTOSMDI L"LicenseStateData.Transfer.SDMI"
  820. typedef struct
  821. {
  822. LPCWSTR pszAction;
  823. LPCWSTR pszLicenseState;
  824. const DRMRIDS *pdrmrids; // Resource ID's
  825. } LICENSE_INFO;
  826. const LICENSE_INFO g_rgLicenseInfo[] =
  827. {
  828. { ACTIONALLOWED_PLAY, LICENSESTATE_PLAY, &g_drmridsPlay },
  829. { ACTIONALLOWED_COPYTOCD, LICENSESTATE_COPYTOCD, &g_drmridsCopyToCD },
  830. { ACTIONALLOWED_COPYTONONSMDI, LICENSESTATE_COPYTONONSMDI, &g_drmridsCopyToNonSDMIDevice },
  831. { ACTIONALLOWED_COPYTOSMDI, LICENSESTATE_COPYTOSMDI, &g_drmridsCopyToSDMIDevice },
  832. };
  833. // We can't use the drm string constants in our const array above (they aren't initialized until after
  834. // our struct is initialized, so they're null), so we redefined the strings
  835. // as #define's ourselves. This function asserts that none of the strings have changed.
  836. void _AssertValidDRMStrings()
  837. {
  838. ASSERT(StrCmp(ACTIONALLOWED_PLAY, g_wszWMDRM_ActionAllowed_Playback) == 0);
  839. ASSERT(StrCmp(ACTIONALLOWED_COPYTOCD, g_wszWMDRM_ActionAllowed_CopyToCD) == 0);
  840. ASSERT(StrCmp(ACTIONALLOWED_COPYTONONSMDI, g_wszWMDRM_ActionAllowed_CopyToNonSDMIDevice) == 0);
  841. ASSERT(StrCmp(ACTIONALLOWED_COPYTOSMDI, g_wszWMDRM_ActionAllowed_CopyToSDMIDevice) == 0);
  842. ASSERT(StrCmp(LICENSESTATE_PLAY, g_wszWMDRM_LicenseState_Playback) == 0);
  843. ASSERT(StrCmp(LICENSESTATE_COPYTOCD, g_wszWMDRM_LicenseState_CopyToCD) == 0);
  844. ASSERT(StrCmp(LICENSESTATE_COPYTONONSMDI, g_wszWMDRM_LicenseState_CopyToNonSDMIDevice) == 0);
  845. ASSERT(StrCmp(LICENSESTATE_COPYTOSMDI, g_wszWMDRM_LicenseState_CopyToSDMIDevice) == 0);
  846. }
  847. BOOL _IsActionPlayback(LPCWSTR pszAction)
  848. {
  849. return (StrCmp(pszAction, ACTIONALLOWED_PLAY) == 0);
  850. }
  851. void AcquireLicenseInformation(IWMDRMReader *pReader, SHMEDIA_AUDIOVIDEOPROPS *pAVProps)
  852. {
  853. WMT_ATTR_DATATYPE dwType;
  854. DWORD dwValue = 0;
  855. WORD cbLength;
  856. WCHAR szValue[MAX_PATH];
  857. _AssertValidDRMStrings();
  858. // For each of the "actions":
  859. for (int i = 0; i < ARRAYSIZE(g_rgLicenseInfo); i++)
  860. {
  861. cbLength = sizeof(dwValue);
  862. // Request the license info.
  863. WM_LICENSE_STATE_DATA licenseState;
  864. cbLength = sizeof(licenseState);
  865. if (SUCCEEDED(pReader->GetDRMProperty(g_rgLicenseInfo[i].pszLicenseState, &dwType, (BYTE*)&licenseState, &cbLength)))
  866. {
  867. DWORD dwCount;
  868. FILETIME ftExpires, ftStarts;
  869. // We should always get at least one DRM_LICENSE_STATE_DATA. This is what ParseDRMStateData assumes.
  870. ASSERTMSG(licenseState.dwNumStates >= 1, "Received WM_LICENSE_STATE_DATA with no states");
  871. // Parse easy special cases first.
  872. if (licenseState.stateData[0].dwCategory == WM_DRM_LICENSE_STATE_NORIGHT)
  873. {
  874. // Not allowed ever. Indicate this.
  875. // Special case for playback action:
  876. if (_IsActionPlayback(g_rgLicenseInfo[i].pszAction))
  877. {
  878. // Not allowed playback. Determine why. Is it because we can never play it, or can we
  879. // just not play it on this computer?
  880. cbLength = sizeof(dwValue);
  881. if (SUCCEEDED(pReader->GetDRMProperty(g_wszWMDRM_IsDRMCached, &dwType, (BYTE*)&dwValue, &cbLength)))
  882. {
  883. UINT uID = (dwValue == 0) ? IDS_DRM_PLAYNORIGHTS : IDS_DRM_PLAYNOPLAYHERE;
  884. LoadString(m_hInst, IDS_DRM_PLAYNOPLAYHERE, szValue, ARRAYSIZE(szValue));
  885. AppendLicenseInfo(pAVProps, szValue);
  886. }
  887. }
  888. else
  889. {
  890. // Regular case:
  891. LoadString(m_hInst, g_rgLicenseInfo[i].pdrmrids->ridNoRights, szValue, ARRAYSIZE(szValue));
  892. AppendLicenseInfo(pAVProps, szValue);
  893. }
  894. }
  895. // Now parse the more complex stuff.
  896. else if (ParseDRMStateData(&licenseState, g_rgLicenseInfo[i].pdrmrids, szValue, ARRAYSIZE(szValue), &dwCount, &ftStarts, &ftExpires) == S_OK)
  897. {
  898. AppendLicenseInfo(pAVProps, szValue);
  899. // Special case for playback action - assign these values:
  900. if (_IsActionPlayback(g_rgLicenseInfo[i].pszAction))
  901. {
  902. pAVProps->ftPlayExpires = ftExpires;
  903. pAVProps->ftPlayStarts = ftStarts;
  904. pAVProps->dwPlayCount = dwCount;
  905. }
  906. }
  907. }
  908. }
  909. }
  910. /**
  911. * Extracts all the "slow" information at once from the file, and places it in the
  912. * SHMEDIA_AUDIOVIDEOPROPS struct.
  913. */
  914. HRESULT CWMPropSetStg::_GetSlowPropertyInfo(SHMEDIA_AUDIOVIDEOPROPS *pAVProps)
  915. {
  916. IWMReader *pReader;
  917. HRESULT hr = CreateReader(IID_PPV_ARG(IWMReader, &pReader));
  918. if (SUCCEEDED(hr))
  919. {
  920. ResetEvent(_hFileOpenEvent);
  921. IWMReaderCallback *pReaderCB;
  922. hr = QueryInterface(IID_PPV_ARG(IWMReaderCallback, &pReaderCB));
  923. if (SUCCEEDED(hr))
  924. {
  925. hr = pReader->Open(_wszFile, pReaderCB, NULL);
  926. pReaderCB->Release();
  927. if (SUCCEEDED(hr))
  928. {
  929. // Wait until file is ready.
  930. WaitForSingleObject(_hFileOpenEvent, INFINITE);
  931. // Indicate whether the content is protected under DRM or not.
  932. WCHAR szValue[128];
  933. LoadString(m_hInst, (_fProtectedContent ? IDS_DRM_ISPROTECTED : IDS_DRM_UNPROTECTED), szValue, ARRAYSIZE(szValue));
  934. AppendLicenseInfo(pAVProps, szValue);
  935. // Try to get license information, if this is protected content
  936. if (_fProtectedContent)
  937. {
  938. IWMDRMReader *pDRMReader;
  939. if (SUCCEEDED(pReader->QueryInterface(IID_PPV_ARG(IWMDRMReader, &pDRMReader))))
  940. {
  941. AcquireLicenseInformation(pDRMReader, pAVProps);
  942. pDRMReader->Release();
  943. }
  944. }
  945. // Let's interate through the streams,
  946. IWMProfile *pProfile;
  947. hr = pReader->QueryInterface(IID_PPV_ARG(IWMProfile, &pProfile));
  948. if (SUCCEEDED(hr))
  949. {
  950. DWORD cStreams;
  951. hr = pProfile->GetStreamCount(&cStreams);
  952. if (SUCCEEDED(hr))
  953. {
  954. BOOL bFoundVideo = FALSE;
  955. BOOL bFoundAudio = FALSE;
  956. for (DWORD dw = 0; dw < cStreams; dw++)
  957. {
  958. IWMStreamConfig *pConfig;
  959. hr = pProfile->GetStream(dw, &pConfig);
  960. if (FAILED(hr))
  961. break;
  962. GUID guidStreamType;
  963. if (SUCCEEDED(pConfig->GetStreamType(&guidStreamType)))
  964. {
  965. if (guidStreamType == MEDIATYPE_Audio)
  966. {
  967. GetAudioProperties(pConfig, pAVProps);
  968. bFoundAudio = TRUE;
  969. }
  970. else if (guidStreamType == MEDIATYPE_Video)
  971. {
  972. GetVideoProperties(pConfig, pAVProps);
  973. bFoundVideo = TRUE;
  974. }
  975. }
  976. pConfig->Release();
  977. if (bFoundVideo && bFoundAudio)
  978. break;
  979. }
  980. }
  981. pProfile->Release();
  982. }
  983. pReader->Close();
  984. }
  985. }
  986. pReader->Release();
  987. }
  988. return hr;
  989. }
  990. void GetVideoPropertiesFromBitmapHeader(BITMAPINFOHEADER *bmi, SHMEDIA_AUDIOVIDEOPROPS *pVideoProps)
  991. {
  992. // bit depth
  993. pVideoProps->wBitDepth = bmi->biBitCount;
  994. // compression.
  995. // Is there an easy way to get this?
  996. // Maybe something with the codec info?
  997. // pVideoProps->pszCompression = new WCHAR[cch];
  998. }
  999. void GetVideoPropertiesFromHeader(VIDEOINFOHEADER *pvih, SHMEDIA_AUDIOVIDEOPROPS *pVideoProps)
  1000. {
  1001. pVideoProps->cx = pvih->rcSource.right - pvih->rcSource.left;
  1002. pVideoProps->cy = pvih->rcSource.bottom - pvih->rcSource.top;
  1003. // Obtain frame rate
  1004. // AvgTimePerFrame is in 100ns units.
  1005. // ISSUE: This value is always zero.
  1006. GetVideoPropertiesFromBitmapHeader(&pvih->bmiHeader, pVideoProps);
  1007. }
  1008. // Can't find def'n for VIDEOINFOHEADER2
  1009. /*
  1010. void GetVideoPropertiesFromHeader2(VIDEOINFOHEADER2 *pvih, SHMEDIA_AUDIOVIDEOPROPS *pVideoProps)
  1011. {
  1012. pVideoProps->cx = pvih->rcSource.right - pvih->rcSource.left;
  1013. pVideoProps->cy = pvih->rcSource.bottom - pvih->rcSource.top;
  1014. GetVideoPropertiesFromBitmapHeader(&pvih->bmiHeader, pVideoProps);
  1015. }
  1016. */
  1017. /**
  1018. * assumes pConfig is a video stream. assumes pVideoProps is zero-inited.
  1019. */
  1020. void GetVideoProperties(IWMStreamConfig *pConfig, SHMEDIA_AUDIOVIDEOPROPS *pVideoProps)
  1021. {
  1022. // bitrate
  1023. pConfig->GetBitrate(&pVideoProps->dwBitrateVideo); // ignore result
  1024. // stream name
  1025. WORD cchStreamName;
  1026. if (SUCCEEDED(pConfig->GetStreamName(NULL, &cchStreamName)))
  1027. {
  1028. pVideoProps->pszStreamNameVideo = new WCHAR[cchStreamName];
  1029. if (pVideoProps->pszStreamNameVideo)
  1030. {
  1031. pConfig->GetStreamName(pVideoProps->pszStreamNameVideo, &cchStreamName); // ignore result
  1032. }
  1033. }
  1034. // stream number
  1035. pConfig->GetStreamNumber(&pVideoProps->wStreamNumberVideo); // ignore result
  1036. // Try to get an IWMMediaProps interface.
  1037. IWMMediaProps *pMediaProps;
  1038. if (SUCCEEDED(pConfig->QueryInterface(IID_PPV_ARG(IWMMediaProps, &pMediaProps))))
  1039. {
  1040. DWORD cbType;
  1041. // Make the first call to establish the size of buffer needed.
  1042. if (SUCCEEDED(pMediaProps->GetMediaType(NULL, &cbType)))
  1043. {
  1044. // Now create a buffer of the appropriate size
  1045. BYTE *pBuf = new BYTE[cbType];
  1046. if (pBuf)
  1047. {
  1048. // Create an appropriate structure pointer to the buffer.
  1049. WM_MEDIA_TYPE *pType = (WM_MEDIA_TYPE*) pBuf;
  1050. // Call the method again to extract the information.
  1051. if (SUCCEEDED(pMediaProps->GetMediaType(pType, &cbType)))
  1052. {
  1053. // Pick up other more obscure information.
  1054. if (IsEqualGUID(pType->formattype, FORMAT_MPEGVideo))
  1055. {
  1056. GetVideoPropertiesFromHeader((VIDEOINFOHEADER*)&((MPEG1VIDEOINFO*)pType->pbFormat)->hdr, pVideoProps);
  1057. }
  1058. else if (IsEqualGUID(pType->formattype, FORMAT_VideoInfo))
  1059. {
  1060. GetVideoPropertiesFromHeader((VIDEOINFOHEADER*)pType->pbFormat, pVideoProps);
  1061. }
  1062. // No def'n available for VIDEOINFOHEADER2
  1063. // else if (IsEqualGUID(pType->formattype, Format_MPEG2Video))
  1064. // {
  1065. // GetVideoPropertiesFromHeader2((VIDEOINFOHEADER2*)&((MPEG1VIDEOINFO2*)&pType->pbFormat)->hdr);
  1066. // }
  1067. // else if (IsEqualGUID(pType->formattype, Format_VideoInfo2))
  1068. // {
  1069. // GetVideoPropertiesFromHeader2((VIDEOINFOHEADER2*)&pType->pbFormat);
  1070. // }
  1071. }
  1072. delete[] pBuf;
  1073. }
  1074. }
  1075. pMediaProps->Release();
  1076. }
  1077. }
  1078. void InitializeAudioVideoProperties(SHMEDIA_AUDIOVIDEOPROPS *pAVProps)
  1079. {
  1080. pAVProps->dwPlayCount = -1; // Indicating no playcount.
  1081. ASSERT(pAVProps->pszLicenseInformation == NULL);
  1082. ASSERT(IsNullTime(&pAVProps->ftPlayStarts));
  1083. ASSERT(IsNullTime(&pAVProps->ftPlayExpires));
  1084. // Audio properties
  1085. ASSERT(pAVProps->pszStreamNameAudio == NULL);
  1086. ASSERT(pAVProps->wStreamNumberAudio == 0);
  1087. ASSERT(pAVProps->nChannels == 0);
  1088. ASSERT(pAVProps->dwBitrateAudio == 0);
  1089. ASSERT(pAVProps->pszCompressionAudio == NULL);
  1090. ASSERT(pAVProps->dwSampleRate == 0);
  1091. ASSERT(pAVProps->lSampleSizeAudio == 0);
  1092. // Video properties
  1093. ASSERT(pAVProps->pszStreamNameVideo == NULL);
  1094. ASSERT(pAVProps->wStreamNumberVideo == 0);
  1095. ASSERT(pAVProps->wBitDepth == 0);
  1096. ASSERT(pAVProps->dwBitrateVideo == 0);
  1097. ASSERT(pAVProps->cx == 0);
  1098. ASSERT(pAVProps->cy == 0);
  1099. ASSERT(pAVProps->pszCompressionVideo == NULL);
  1100. ASSERT(pAVProps->dwFrames == 0);
  1101. ASSERT(pAVProps->dwFrameRate == 0);
  1102. }
  1103. void FreeAudioVideoProperties(SHMEDIA_AUDIOVIDEOPROPS *pAVProps)
  1104. {
  1105. if (pAVProps->pszStreamNameVideo)
  1106. {
  1107. delete[] pAVProps->pszStreamNameVideo;
  1108. }
  1109. if (pAVProps->pszCompressionVideo)
  1110. {
  1111. delete[] pAVProps->pszCompressionVideo;
  1112. }
  1113. if (pAVProps->pszStreamNameAudio)
  1114. {
  1115. delete[] pAVProps->pszStreamNameAudio;
  1116. }
  1117. if (pAVProps->pszCompressionAudio)
  1118. {
  1119. delete[] pAVProps->pszCompressionAudio;
  1120. }
  1121. if (pAVProps->pszLicenseInformation)
  1122. {
  1123. CoTaskMemFree(pAVProps->pszLicenseInformation);
  1124. }
  1125. }
  1126. /**
  1127. * assumes pConfig is an audio stream. assumes pAudioProps is zero-inited.
  1128. */
  1129. void GetAudioProperties(IWMStreamConfig *pConfig, SHMEDIA_AUDIOVIDEOPROPS *pAudioProps)
  1130. {
  1131. // bitrate
  1132. pConfig->GetBitrate(&pAudioProps->dwBitrateAudio); // ignore result
  1133. // stream name
  1134. WORD cchStreamName;
  1135. if (SUCCEEDED(pConfig->GetStreamName(NULL, &cchStreamName)))
  1136. {
  1137. pAudioProps->pszStreamNameAudio = new WCHAR[cchStreamName];
  1138. if (pAudioProps->pszStreamNameAudio)
  1139. {
  1140. pConfig->GetStreamName(pAudioProps->pszStreamNameAudio, &cchStreamName); // ignore result
  1141. }
  1142. }
  1143. // stream number
  1144. pConfig->GetStreamNumber(&pAudioProps->wStreamNumberAudio); // ignore result
  1145. // Try to get an IWMMediaProps interface.
  1146. IWMMediaProps *pMediaProps;
  1147. if (SUCCEEDED(pConfig->QueryInterface(IID_PPV_ARG(IWMMediaProps, &pMediaProps))))
  1148. {
  1149. DWORD cbType;
  1150. // Make the first call to establish the size of buffer needed.
  1151. if (SUCCEEDED(pMediaProps->GetMediaType(NULL, &cbType)))
  1152. {
  1153. // Now create a buffer of the appropriate size
  1154. BYTE *pBuf = new BYTE[cbType];
  1155. if (pBuf)
  1156. {
  1157. // Create an appropriate structure pointer to the buffer.
  1158. WM_MEDIA_TYPE *pType = (WM_MEDIA_TYPE*)pBuf;
  1159. // Call the method again to extract the information.
  1160. if (SUCCEEDED(pMediaProps->GetMediaType(pType, &cbType)))
  1161. {
  1162. if (pType->bFixedSizeSamples) // Assuming lSampleSize only valid if fixed sample sizes
  1163. {
  1164. pAudioProps->lSampleSizeAudio = pType->lSampleSize;
  1165. }
  1166. // Pick up other more obscure information.
  1167. if (IsEqualGUID(pType->formattype, FORMAT_WaveFormatEx))
  1168. {
  1169. WAVEFORMATEX *pWaveFmt = (WAVEFORMATEX*)pType->pbFormat;
  1170. pAudioProps->nChannels = pWaveFmt->nChannels;
  1171. pAudioProps->dwSampleRate = pWaveFmt->nSamplesPerSec;
  1172. // Setting this again if we got in here.
  1173. // For mp3s and wmas at least, this number is accurate, while pType->lSampleSize is bogus.
  1174. pAudioProps->lSampleSizeAudio = pWaveFmt->wBitsPerSample;
  1175. }
  1176. // How do we get compression?
  1177. }
  1178. delete[] pBuf;
  1179. }
  1180. }
  1181. pMediaProps->Release();
  1182. }
  1183. }
  1184. // Returns a *pHeaderInfo and success if it opened an editor and obtained a IWMHeaderInfo.
  1185. HRESULT CWMPropSetStg::_OpenHeaderInfo(IWMHeaderInfo **ppHeaderInfo, BOOL fReadingOnly)
  1186. {
  1187. IWMMetadataEditor *pEditor;
  1188. *ppHeaderInfo = NULL;
  1189. // use the "editor" object as it is much MUCH faster than the reader
  1190. HRESULT hr = WMCreateEditor(&pEditor);
  1191. if (SUCCEEDED(hr))
  1192. {
  1193. IWMMetadataEditor2 *pmde2;
  1194. if (fReadingOnly && SUCCEEDED(pEditor->QueryInterface(IID_PPV_ARG(IWMMetadataEditor2, &pmde2))))
  1195. {
  1196. hr = pmde2->OpenEx(_wszFile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE);
  1197. pmde2->Release();
  1198. }
  1199. else
  1200. {
  1201. hr = pEditor->Open(_wszFile);
  1202. }
  1203. if (SUCCEEDED(hr))
  1204. {
  1205. hr = pEditor->QueryInterface(IID_PPV_ARG(IWMHeaderInfo, ppHeaderInfo));
  1206. if (FAILED(hr))
  1207. {
  1208. pEditor->Close();
  1209. }
  1210. }
  1211. // Always release this particular ref to the editor. Don't need it anymore.
  1212. pEditor->Release();
  1213. // If we got here with SUCCEESS, it means we have an open editor, and a ref to the metadata editor.
  1214. ASSERT((FAILED(hr) && (*ppHeaderInfo == NULL)) || (SUCCEEDED(hr) && (*ppHeaderInfo != NULL)));
  1215. }
  1216. return hr;
  1217. }
  1218. // Cleans up after _OpenHeaderInfo (closes the editor, etc...)
  1219. // fFlush Flush the header?
  1220. HRESULT _CloseHeaderInfo(IWMHeaderInfo *pHeaderInfo, BOOL fFlush)
  1221. {
  1222. HRESULT hr = S_OK;
  1223. if (pHeaderInfo)
  1224. {
  1225. // Close the editor.
  1226. IWMMetadataEditor *pEditor;
  1227. hr = pHeaderInfo->QueryInterface(IID_PPV_ARG(IWMMetadataEditor, &pEditor));
  1228. ASSERT(SUCCEEDED(hr)); // QI is symmetric, so this always succeeds.
  1229. if (SUCCEEDED(hr))
  1230. {
  1231. if (fFlush)
  1232. {
  1233. hr = pEditor->Flush();
  1234. }
  1235. pEditor->Close();
  1236. pEditor->Release();
  1237. }
  1238. pHeaderInfo->Release();
  1239. }
  1240. return hr;
  1241. }
  1242. HRESULT CWMPropSetStg::FlushChanges(REFFMTID fmtid, LONG cNumProps, const COLMAP **pcmapInfo, PROPVARIANT *pVarProps, BOOL *pbDirtyFlags)
  1243. {
  1244. if (!_bIsWritable)
  1245. return STG_E_ACCESSDENIED;
  1246. IWMHeaderInfo *phi;
  1247. HRESULT hr = _OpenHeaderInfo(&phi, HI_READWRITE);
  1248. if (SUCCEEDED(hr))
  1249. {
  1250. BOOL bFlush = FALSE;
  1251. for (LONG i = 0; i < cNumProps; i++)
  1252. {
  1253. if (pbDirtyFlags[i])
  1254. {
  1255. HRESULT hrFlush = E_FAIL;
  1256. if ((pcmapInfo[i]->vt == pVarProps[i].vt) || (VT_EMPTY == pVarProps[i].vt) || (VT_NULL == pVarProps[i].vt)) // VT_EMPTY/VT_NULL means remove property
  1257. {
  1258. hrFlush = _FlushProperty(phi, pcmapInfo[i], &pVarProps[i]);
  1259. }
  1260. else
  1261. {
  1262. PROPVARIANT var;
  1263. // Don't need to call PropVariantInit
  1264. hrFlush = PropVariantCopy(&var, &pVarProps[i]);
  1265. if (SUCCEEDED(hrFlush))
  1266. {
  1267. hrFlush = CoerceProperty(&var, pcmapInfo[i]->vt);
  1268. if (SUCCEEDED(hrFlush))
  1269. {
  1270. hrFlush = _FlushProperty(phi, pcmapInfo[i], &var);
  1271. }
  1272. PropVariantClear(&var);
  1273. }
  1274. }
  1275. if (FAILED(hrFlush))
  1276. {
  1277. // Take note of any failure case, so we have something to return.
  1278. hr = hrFlush;
  1279. }
  1280. }
  1281. }
  1282. // Specify the flush bit if we succeeded in writing all the properties.
  1283. HRESULT hrClose = _CloseHeaderInfo(phi, SUCCEEDED(hr));
  1284. // If for some reason the Flush failed (in _CloseHeaderInfo), we'll fail.
  1285. if (FAILED(hrClose))
  1286. {
  1287. hr = hrClose;
  1288. }
  1289. }
  1290. return hr;
  1291. }
  1292. #define MAX_PROP_LENGTH 4096 // big enough for large props like lyrics.
  1293. HRESULT CWMPropSetStg::_FlushProperty(IWMHeaderInfo *phi, const COLMAP *pPInfo, PROPVARIANT *pvar)
  1294. {
  1295. WMT_ATTR_DATATYPE datatype;
  1296. BYTE buffer[MAX_PROP_LENGTH];
  1297. WORD cbLen = ARRAYSIZE(buffer);
  1298. HRESULT hr = E_FAIL;
  1299. // Handle special properties first:
  1300. // The Track property can exist as both the newer 1-based WM/TrackNumber, or the old
  1301. // 0-based WM/Track
  1302. if (IsEqualSCID(SCID_MUSIC_Track, *pPInfo->pscid))
  1303. {
  1304. if ((pvar->vt != VT_EMPTY) && (pvar->vt != VT_NULL))
  1305. {
  1306. ASSERT(pvar->vt = VT_UI4);
  1307. if (pvar->ulVal > 0) // Track number must be greater than zero - don't want to overflow 0-based buffer
  1308. {
  1309. // Decrement the track number for writing to the old zero-based property
  1310. pvar->ulVal--;
  1311. HRESULT hr1 = WMTFromPropVariant(buffer, &cbLen, &datatype, pvar);
  1312. if (SUCCEEDED(hr1))
  1313. {
  1314. hr1 = phi->SetAttribute(0, TRACK_ZERO_BASED, datatype, buffer, cbLen);
  1315. }
  1316. pvar->ulVal++; // back to 1-based
  1317. HRESULT hr2 = WMTFromPropVariant(buffer, &cbLen, &datatype, pvar);
  1318. if (SUCCEEDED(hr2))
  1319. {
  1320. hr2 = phi->SetAttribute(0, TRACK_ONE_BASED, datatype, buffer, cbLen);
  1321. }
  1322. // Return success if one of them worked.
  1323. hr = (SUCCEEDED(hr1) || SUCCEEDED(hr2)) ? S_OK : hr1;
  1324. }
  1325. }
  1326. else
  1327. {
  1328. hr = S_OK; // Someone tried to remove the track property, but we'll just fail silently, since we can't return a good error.
  1329. }
  1330. }
  1331. else if (IsEqualSCID(SCID_DRM_Protected, *pPInfo->pscid))
  1332. {
  1333. // We should never get here. Protected is read only.
  1334. hr = E_INVALIDARG;
  1335. }
  1336. else
  1337. {
  1338. // Regular properties.
  1339. if ((pvar->vt == VT_EMPTY) || (pvar->vt == VT_NULL))
  1340. {
  1341. // Try to remove this property.
  1342. // Note: Doesn't matter what we pass in for datatype, since we're providing NULL as the value.
  1343. hr = phi->SetAttribute(0, _GetSDKName(pPInfo), WMT_TYPE_STRING, NULL, 0);
  1344. // This is weak.
  1345. // The WMSDK has a bug where if you try to remove a property that has already been removed, it will return
  1346. // an error (ASF_E_NOTFOUND for wma files, E_FAIL for mp3s). So for any errors, we'll return success.
  1347. if (FAILED(hr))
  1348. {
  1349. hr = S_OK;
  1350. }
  1351. }
  1352. else
  1353. {
  1354. hr = WMTFromPropVariant(buffer, &cbLen, &datatype, pvar);
  1355. if (SUCCEEDED(hr))
  1356. {
  1357. hr = phi->SetAttribute(0, _GetSDKName(pPInfo), datatype, buffer, cbLen);
  1358. }
  1359. }
  1360. }
  1361. return hr;
  1362. }
  1363. // We need to check for protected content ahead of time.
  1364. HRESULT CWMPropSetStg::_PreCheck()
  1365. {
  1366. HRESULT hr = _PopulatePropertySet();
  1367. if (SUCCEEDED(hr))
  1368. {
  1369. if (_fProtectedContent && _bIsWritable)
  1370. {
  1371. _bIsWritable = FALSE;
  1372. hr = STG_E_STATUS_COPY_PROTECTION_FAILURE;
  1373. }
  1374. }
  1375. return hr;
  1376. }
  1377. HRESULT CWMPropSetStg::_PopulatePropertySet()
  1378. {
  1379. HRESULT hr = E_FAIL;
  1380. if (_wszFile[0] == 0)
  1381. {
  1382. hr = STG_E_INVALIDNAME;
  1383. }
  1384. else if (!_bHasBeenPopulated)
  1385. {
  1386. IWMHeaderInfo *phi;
  1387. hr = _OpenHeaderInfo(&phi, HI_READONLY);
  1388. if (SUCCEEDED(hr))
  1389. {
  1390. CEnumAllProps enumAllProps(_pPropStgInfo, _cPropertyStorages);
  1391. const COLMAP *pPInfo = enumAllProps.Next();
  1392. while (pPInfo)
  1393. {
  1394. LPCWSTR pszPropName = _GetSDKName(pPInfo);
  1395. // Skip it if this is not one of the properties available quickly through
  1396. // IWMHeaderInfo
  1397. if (_IsHeaderProperty(pPInfo))
  1398. {
  1399. // Get length of buffer needed for property value
  1400. WMT_ATTR_DATATYPE proptype;
  1401. UCHAR buf[MAX_PROP_LENGTH];
  1402. WORD cbData = sizeof(buf);
  1403. WORD wStreamNum = 0;
  1404. if (_PopulateSpecialProperty(phi, pPInfo) == S_FALSE)
  1405. {
  1406. // Not a special property
  1407. ASSERT(_GetSDKName(pPInfo)); // If not def'd as a special prop, must have an SDK name
  1408. // Note: this call will fail if the buffer is not big enough. This means that
  1409. // we will not get values for potentially really large properties like lyrics.
  1410. hr = phi->GetAttributeByName(&wStreamNum, pszPropName, &proptype, buf, &cbData);
  1411. if (SUCCEEDED(hr))
  1412. {
  1413. hr = _SetPropertyFromWMT(pPInfo, proptype, cbData ? buf : NULL, cbData);
  1414. }
  1415. else
  1416. {
  1417. // Is it supposed to be a string property? If so, provide a NULL string.
  1418. // ISSUE: we may want to revisit this policy, because of changes in docprop
  1419. if ((pPInfo->vt == VT_LPSTR) || (pPInfo->vt == VT_LPWSTR))
  1420. {
  1421. hr = _SetPropertyFromWMT(pPInfo, WMT_TYPE_STRING, NULL, 0);
  1422. }
  1423. }
  1424. }
  1425. }
  1426. pPInfo = enumAllProps.Next();
  1427. }
  1428. _PostProcess();
  1429. _CloseHeaderInfo(phi, FALSE);
  1430. }
  1431. _bHasBeenPopulated = TRUE;
  1432. // even if we couldn't create the metadata editor, we might be able to open a reader (which we'll do later)
  1433. // So we can return S_OK here. However, it would be nice to know ahead of time if opening a Reader
  1434. // will work. Oh well.
  1435. _hrPopulated = S_OK;
  1436. }
  1437. return _hrPopulated;
  1438. }
  1439. /**
  1440. * Takes a quick peek at what the current value of this property is (does not
  1441. * force a slow property to be populated), returning a reference to the actual
  1442. * value (so no PropVariantClear is necessary)
  1443. */
  1444. HRESULT CWMPropSetStg::_QuickLookup(const COLMAP *pPInfo, PROPVARIANT **ppvar)
  1445. {
  1446. CMediaPropStorage *pps;
  1447. HRESULT hr = _ResolveFMTID(pPInfo->pscid->fmtid, &pps);
  1448. if (SUCCEEDED(hr))
  1449. {
  1450. PROPSPEC spec;
  1451. spec.ulKind = PRSPEC_PROPID;
  1452. spec.propid = pPInfo->pscid->pid;
  1453. hr = pps->QuickLookup(&spec, ppvar);
  1454. }
  1455. return hr;
  1456. }
  1457. /**
  1458. * Any special actions to take after initial property population.
  1459. */
  1460. void CWMPropSetStg::_PostProcess()
  1461. {
  1462. PROPVARIANT *pvar;
  1463. // 1) If this file is protected, mark this. (we don't allow writes to protected files)
  1464. if (SUCCEEDED(_QuickLookup(&g_CM_Protected, &pvar)))
  1465. {
  1466. if (pvar->vt == VT_BOOL)
  1467. {
  1468. _fProtectedContent = pvar->boolVal;
  1469. }
  1470. }
  1471. // 2) Mark if Duration or Bitrate were retrieved. If they weren't, then we'll consider them
  1472. // "slow" properties for this file.
  1473. if (SUCCEEDED(_QuickLookup(&g_CM_Duration, &pvar)))
  1474. {
  1475. _fDurationSlow = (pvar->vt == VT_EMPTY);
  1476. }
  1477. if (SUCCEEDED(_QuickLookup(&g_CM_Bitrate, &pvar)))
  1478. {
  1479. _fBitrateSlow = (pvar->vt == VT_EMPTY);
  1480. }
  1481. }
  1482. /**
  1483. * Special properties need some additional action taken.
  1484. *
  1485. * Track: Use 1-based track # if available, 0-based otherwise.
  1486. */
  1487. HRESULT CWMPropSetStg::_PopulateSpecialProperty(IWMHeaderInfo *phi, const COLMAP *pPInfo)
  1488. {
  1489. WMT_ATTR_DATATYPE proptype;
  1490. UCHAR buf[1024]; // big enough
  1491. WORD cbData = sizeof(buf);
  1492. WORD wStreamNum = 0;
  1493. HRESULT hr;
  1494. if (IsEqualSCID(SCID_MUSIC_Track, *pPInfo->pscid))
  1495. {
  1496. // Try to get 1-based track.
  1497. hr = phi->GetAttributeByName(&wStreamNum, TRACK_ONE_BASED, &proptype, buf, &cbData);
  1498. if (FAILED(hr))
  1499. {
  1500. // Nope, so try to get 0-based track and increment by one.
  1501. hr = phi->GetAttributeByName(&wStreamNum, TRACK_ZERO_BASED, &proptype, buf, &cbData);
  1502. if (SUCCEEDED(hr))
  1503. {
  1504. // We can't just increment the value so easily, because the value could be of type
  1505. // WMT_TYPE_STRING or WMT_TYPE_DWORD (track # is string for some mp3's)
  1506. // So we'll go through the same conversion process as happens when we call _SetPropertyFromWMT
  1507. PROPVARIANT varTemp = {0};
  1508. hr = PropVariantFromWMT(buf, cbData, proptype, &varTemp, VT_UI4);
  1509. if (SUCCEEDED(hr))
  1510. {
  1511. // Got a VT_UI4, we know how to increment that.
  1512. varTemp.ulVal++;
  1513. // Now convert back to a WMT_ATTR that we can provide to _SetPropertyFromWMT
  1514. hr = WMTFromPropVariant(buf, &cbData, &proptype, &varTemp);
  1515. PropVariantClear(&varTemp);
  1516. }
  1517. }
  1518. }
  1519. if (SUCCEEDED(hr))
  1520. {
  1521. _SetPropertyFromWMT(pPInfo, proptype, cbData ? buf : NULL, cbData);
  1522. }
  1523. }
  1524. else
  1525. {
  1526. hr = S_FALSE;
  1527. }
  1528. return hr;
  1529. }
  1530. HRESULT CWMPropSetStg::_SetPropertyFromWMT(const COLMAP *pPInfo, WMT_ATTR_DATATYPE attrDatatype, UCHAR *pData, WORD cbSize)
  1531. {
  1532. PROPSPEC spec;
  1533. spec.ulKind = PRSPEC_PROPID;
  1534. spec.propid = pPInfo->pscid->pid;
  1535. PROPVARIANT var = {0};
  1536. if (SUCCEEDED(PropVariantFromWMT(pData, cbSize, attrDatatype, &var, pPInfo->vt)))
  1537. {
  1538. _PopulateProperty(pPInfo, &var);
  1539. PropVariantClear(&var);
  1540. }
  1541. return S_OK;
  1542. }
  1543. // Retrieves the name used by the WMSDK in IWMHeaderInfo->Get/SetAttribute
  1544. LPCWSTR CWMPropSetStg::_GetSDKName(const COLMAP *pPInfo)
  1545. {
  1546. for (int i = 0; i < ARRAYSIZE(g_rgSCIDToSDKName); i++)
  1547. {
  1548. if (IsEqualSCID(*pPInfo->pscid, *g_rgSCIDToSDKName[i].pscid))
  1549. return g_rgSCIDToSDKName[i].pszSDKName;
  1550. }
  1551. return NULL;
  1552. }
  1553. // Is it one of the properties that can be accessed via IWMHeaderInfo?
  1554. BOOL CWMPropSetStg::_IsHeaderProperty(const COLMAP *pPInfo)
  1555. {
  1556. for (int i = 0; i < ARRAYSIZE(g_rgSCIDToSDKName); i++)
  1557. {
  1558. if (IsEqualSCID(*pPInfo->pscid, *g_rgSCIDToSDKName[i].pscid))
  1559. return TRUE;
  1560. }
  1561. return FALSE;
  1562. }
  1563. // Creates
  1564. // For audio files (mp3, wma, ....)
  1565. STDAPI CWMAPropSetStg_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
  1566. {
  1567. HRESULT hr;
  1568. CWMAPropSetStg *pPropSetStg = new CWMAPropSetStg();
  1569. if (pPropSetStg)
  1570. {
  1571. hr = pPropSetStg->Init();
  1572. if (SUCCEEDED(hr))
  1573. {
  1574. hr = pPropSetStg->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
  1575. }
  1576. pPropSetStg->Release();
  1577. }
  1578. else
  1579. {
  1580. *ppunk = NULL;
  1581. hr = E_OUTOFMEMORY;
  1582. }
  1583. return hr;
  1584. }
  1585. // For video/audio files (wmv, wma, ... )
  1586. STDAPI CWMVPropSetStg_CreateInstance(IUnknown *punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
  1587. {
  1588. HRESULT hr;
  1589. CWMVPropSetStg *pPropSetStg = new CWMVPropSetStg();
  1590. if (pPropSetStg)
  1591. {
  1592. hr = pPropSetStg->Init();
  1593. if (SUCCEEDED(hr))
  1594. {
  1595. hr = pPropSetStg->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
  1596. }
  1597. pPropSetStg->Release();
  1598. }
  1599. else
  1600. {
  1601. *ppunk = NULL;
  1602. hr = E_OUTOFMEMORY;
  1603. }
  1604. return hr;
  1605. }