Leaked source code of windows server 2003
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.

622 lines
18 KiB

  1. //==========================================================================;
  2. //
  3. // Copyright (c) Microsoft Corporation 1998-2000.
  4. //
  5. //--------------------------------------------------------------------------;
  6. //
  7. // dvdprot.cpp : Implementation of CDVDProt
  8. //
  9. //
  10. //
  11. // URL ::= DVD | DVD:[//<path>?] [<address>]
  12. // address ::= <title> | <title>/<chapter>[-<end_chapter>] | <title>/<time>[-<end_time>]
  13. // path ::= <unc_path> | <drive_letter>:/<directory_path>
  14. // title ::= [digit] digit
  15. // chapter ::= [ [digit] digit] digit
  16. // time ::= [<hours>:] [<minutes>:] [<seconds>:] <frames>
  17. // hours := [digit | 0] digit
  18. // minutes:= [digit | 0] digit
  19. // seconds:= [digit | 0] digit
  20. // frames:= [digit | 0] digit
  21. //
  22. // DVD: play first DVD found, enumerating from drive D:
  23. // DVD:2 play title 2 (in first DVD found)
  24. // DVD:5/13 play chapter 13 of title 5 (in first DVD found)
  25. // DVD:7/9:05-13:23 play from 7 seconds 5 frames to 13 seconds 23 frames in title 7
  26. // DVD:7/00:00:12:05-00:00:17:23 (strict version of timecode)
  27. // DVD://myshare/dvd?9 play title 9 from the DVD-Video volume stored in the dvd directory of share
  28. // DVD://f:/video_ts play the DVD-Video volume in the video_ts directory of drive F:
  29. #include "stdafx.h"
  30. #ifndef TUNING_MODEL_ONLY
  31. #include "devices.h"
  32. #include "msvidwebdvd.h"
  33. #include "vidprot.h"
  34. #define MAX_FIELDS 10
  35. HRESULT CMSVidWebDVD::ParseDVDPath(LPWSTR pPath)
  36. {
  37. WCHAR wsUncPath[MAX_PATH];
  38. int nFields, i;
  39. DVD_HMSF_TIMECODE tc;
  40. BSTR bstrTime = NULL;
  41. BSTR bstrEndTime = NULL;
  42. long Fields[MAX_FIELDS];
  43. long Delimiters[MAX_FIELDS];
  44. HRESULT hr = S_OK;
  45. // recognize "DVD:" at the beginning of string
  46. // note: we also allow "DVD" for compatibility with old code
  47. if (!pPath)
  48. {
  49. return E_INVALIDARG;
  50. }
  51. if (_wcsicmp(pPath, L"DVD") == 0)
  52. {
  53. pPath += 3;
  54. }
  55. else if (_wcsnicmp(pPath, L"DVD:", 4) == 0)
  56. {
  57. pPath += 4;
  58. }
  59. else
  60. {
  61. return E_INVALIDARG;
  62. }
  63. // determine if a unc path follows (starts with "//")
  64. if (wcsncmp(pPath, L"//", 2) == 0)
  65. {
  66. // determine if it is followed by a share name or a drive letter
  67. if (iswalpha(pPath[2]) && pPath[3] == L':')
  68. {
  69. // filter out the two foward slashes in front of drive letter
  70. pPath += 2;
  71. }
  72. // copy over the remaining unc path
  73. if(wcslen(pPath) >= MAX_PATH){
  74. // pPath is longer than wsUncPath so it will be truncated
  75. }
  76. // Could chop off a char if wsclen(pPath) == MAX_PATH
  77. lstrcpyn(wsUncPath, pPath, MAX_PATH);
  78. // search for the ending '?'; replacing forward slash with backslash
  79. i = 0;
  80. while (wsUncPath[i] != L'?' && wsUncPath[i] != 0)
  81. {
  82. if (wsUncPath[i] == L'/')
  83. {
  84. wsUncPath[i] = L'\\';
  85. }
  86. i++;
  87. }
  88. if (wsUncPath[i] == L'?')
  89. {
  90. // replace ? with NULL to truncate the rest of the string
  91. wsUncPath[i] = 0;
  92. pPath += i+1; // advance pointer pass the ?
  93. }
  94. else
  95. {
  96. // the entire string is the unc without the ?
  97. // advance pointer so that it points to the NULL
  98. pPath += i;
  99. }
  100. // append VIDEO_TS directory if only the drive is given
  101. // wsUncPath is an array of WCHARs MAX_PATH in length
  102. if (wcslen(wsUncPath) == 2 && iswalpha(wsUncPath[0]) && wsUncPath[1] == L':')
  103. {
  104. (void)StringCchCat(wsUncPath, SIZEOF_CH(wsUncPath), L"\\video_ts");
  105. //wcscat(wsUncPath, L"\\video_ts");
  106. }
  107. // save the path to dvd directory
  108. if (m_urlInfo.bstrPath != NULL)
  109. {
  110. SysFreeString(m_urlInfo.bstrPath);
  111. }
  112. m_urlInfo.bstrPath = SysAllocString(wsUncPath);
  113. }
  114. // if no title or chapter is set, let it play with default settings
  115. if (*pPath == 0)
  116. {
  117. return hr;
  118. }
  119. // parse address section
  120. // address ::= <title> | <title>/<chapter>[-<end_chapter>] | <title>/<time>[-<end_time>]
  121. // fetch a two-digit title number
  122. m_urlInfo.lTitle = ParseNumber(pPath);
  123. // retrieve all the numerical fields and Delimiters
  124. nFields = 0;
  125. while (nFields < MAX_FIELDS && *pPath != 0)
  126. {
  127. Delimiters[nFields] = *pPath++;
  128. Fields[nFields] = ParseNumber(pPath);
  129. nFields++;
  130. }
  131. // analyze the fields
  132. // find if there is a '-' with and ending chapter/time, and ':' indicating time
  133. int nPosHyphen = nFields;
  134. bool fEndSpecified = false;
  135. bool fTimeSpecified = false;
  136. for (i=0; i<nFields; i++)
  137. {
  138. if (L'-' == Delimiters[i])
  139. {
  140. nPosHyphen = i;
  141. fEndSpecified = true;
  142. }
  143. if (L':' == Delimiters[i])
  144. {
  145. fTimeSpecified = true;
  146. }
  147. }
  148. // title
  149. if (nFields == 0)
  150. {
  151. // title is specified, but no starting chapter or time
  152. m_urlInfo.enumRef = DVD_Playback_Title;
  153. }
  154. else
  155. {
  156. if (Delimiters[0] != L'/')
  157. {
  158. return E_INVALIDARG;
  159. }
  160. if (fTimeSpecified)
  161. {
  162. // get start time
  163. // make sure there are 1 to 4 time fields
  164. if (nPosHyphen < 1 || nPosHyphen > 4)
  165. {
  166. return E_INVALIDARG;
  167. }
  168. else
  169. {
  170. for (i=1; i < nPosHyphen; i++)
  171. {
  172. if (Delimiters[i] != L':')
  173. {
  174. return E_INVALIDARG;
  175. }
  176. }
  177. tc.bHours = 0;
  178. tc.bMinutes = 0;
  179. tc.bSeconds = 0;
  180. tc.bFrames = 0;
  181. // fill up to 4 fields
  182. // shifting values from the lower field up
  183. for (i=0; i < nPosHyphen; i++)
  184. {
  185. tc.bHours = tc.bMinutes;
  186. tc.bMinutes = tc.bSeconds;
  187. tc.bSeconds = tc.bFrames;
  188. tc.bFrames = Fields[i];
  189. }
  190. m_urlInfo.ulTime = *(ULONG *)(&tc);
  191. }
  192. // end time
  193. if (fEndSpecified)
  194. {
  195. // make sure there are 1 to 4 time fields
  196. if (nFields-nPosHyphen < 1 || nFields-nPosHyphen > 4)
  197. {
  198. return E_INVALIDARG;
  199. }
  200. else
  201. {
  202. for (i=nPosHyphen+1; i < nFields; i++)
  203. {
  204. if (Delimiters[i] != L':')
  205. {
  206. return E_INVALIDARG;
  207. }
  208. }
  209. tc.bHours = 0;
  210. tc.bMinutes = 0;
  211. tc.bSeconds = 0;
  212. tc.bFrames = 0;
  213. for (i=nPosHyphen; i < nFields; i++)
  214. {
  215. tc.bHours = tc.bMinutes;
  216. tc.bMinutes = tc.bSeconds;
  217. tc.bSeconds = tc.bFrames;
  218. tc.bFrames = Fields[i];
  219. }
  220. m_urlInfo.ulEndTime = *(ULONG *)(&tc);
  221. m_urlInfo.enumRef = DVD_Playback_Time_Range;
  222. }
  223. }
  224. else
  225. {
  226. // only start time specified, no end time
  227. m_urlInfo.enumRef = DVD_Playback_Time;
  228. }
  229. }
  230. else
  231. {
  232. // chapter specified
  233. if (nPosHyphen != 1)
  234. {
  235. return E_INVALIDARG;
  236. }
  237. m_urlInfo.lChapter = Fields[0];
  238. if (fEndSpecified)
  239. {
  240. if (nFields-nPosHyphen != 1)
  241. {
  242. return E_INVALIDARG;
  243. }
  244. m_urlInfo.lEndChapter = Fields[1];
  245. if (m_urlInfo.lEndChapter < m_urlInfo.lChapter)
  246. {
  247. return E_INVALIDARG;
  248. }
  249. m_urlInfo.enumRef = DVD_Playback_Chapter_Range;
  250. }
  251. else
  252. {
  253. m_urlInfo.enumRef = DVD_Playback_Chapter;
  254. }
  255. }
  256. }
  257. return hr;
  258. }
  259. void CMSVidWebDVD::DeleteUrlInfo()
  260. {
  261. if (m_urlInfo.bstrPath != NULL)
  262. {
  263. SysFreeString(m_urlInfo.bstrPath);
  264. }
  265. ZeroMemory(&m_urlInfo, sizeof(m_urlInfo));
  266. m_fUrlInfoSet = false;
  267. }
  268. HRESULT CMSVidWebDVD::SetPlaybackFromUrlInfo()
  269. {
  270. HRESULT hr = S_OK;
  271. BSTR bstrTime, bstrEndTime;
  272. if (!m_fUrlInfoSet)
  273. {
  274. return S_OK;
  275. }
  276. // clear this flag to prevent this function to be called recursively
  277. m_fUrlInfoSet = false;
  278. switch (m_urlInfo.enumRef)
  279. {
  280. case DVD_Playback_Title:
  281. hr = PlayTitle(m_urlInfo.lTitle);
  282. break;
  283. case DVD_Playback_Chapter:
  284. hr = PlayChapterInTitle(m_urlInfo.lTitle, m_urlInfo.lChapter);
  285. break;
  286. case DVD_Playback_Chapter_Range:
  287. hr = PlayChaptersAutoStop(m_urlInfo.lTitle, m_urlInfo.lChapter,
  288. m_urlInfo.lEndChapter-m_urlInfo.lChapter+1);
  289. break;
  290. case DVD_Playback_Time:
  291. DVDTime2bstr((DVD_HMSF_TIMECODE *)&(m_urlInfo.ulTime), &bstrTime);
  292. hr = PlayAtTimeInTitle(m_urlInfo.lTitle, bstrTime);
  293. SysFreeString(bstrTime);
  294. break;
  295. case DVD_Playback_Time_Range:
  296. DVDTime2bstr((DVD_HMSF_TIMECODE *)&(m_urlInfo.ulTime), &bstrTime);
  297. DVDTime2bstr((DVD_HMSF_TIMECODE *)&(m_urlInfo.ulEndTime), &bstrEndTime);
  298. hr = PlayPeriodInTitleAutoStop(m_urlInfo.lTitle, bstrTime, bstrEndTime);
  299. SysFreeString(bstrTime);
  300. SysFreeString(bstrEndTime);
  301. break;
  302. default:
  303. // just let play with the default settings
  304. break;
  305. }
  306. // once the urlInfo has been applied, clear the urlInfo
  307. DeleteUrlInfo();
  308. return hr;
  309. }
  310. HRESULT CMSVidWebDVD::SetDirectoryFromUrlInfo()
  311. {
  312. HRESULT hr = S_OK;
  313. if (!m_fUrlInfoSet || !(m_urlInfo.bstrPath) )
  314. {
  315. return hr;
  316. }
  317. hr = put_DVDDirectory(m_urlInfo.bstrPath);
  318. // clear up the path to prevent this function to be called recursively
  319. SysFreeString(m_urlInfo.bstrPath);
  320. m_urlInfo.bstrPath.Empty();
  321. return hr;
  322. }
  323. // fetch a positive integer from string p, upto nMaxDigits or until a non-digit char is reached
  324. // unlimited nubmer of digits if 0 is passed in nMaxDigits
  325. // advance the the pointer p by the number of chars interpreted.
  326. // it would return 0 if no digit present
  327. int CMSVidWebDVD::ParseNumber(LPWSTR& p, int nMaxDigits)
  328. {
  329. int nDigits = 0;
  330. int nNumber = 0;
  331. while ((nDigits < nMaxDigits || nMaxDigits <= 0) && iswdigit(*p))
  332. {
  333. nNumber = nNumber * 10 + (*p - L'0');
  334. p++;
  335. nDigits++;
  336. }
  337. return nNumber;
  338. }
  339. /////////////////////////////////////////////////////////////////////////////
  340. /////////////////////////////////////////////////////////////////////////////
  341. // CDVDProt
  342. /////////////////////////////////////////////////////////////////////////////
  343. /////////////////////////////////////////////////////////////////////////////
  344. /////////////////////////////////////////////////////////////////////////////
  345. // CDVDProt -- IInternetProtocolRoot
  346. STDMETHODIMP CDVDProt::Start(LPCWSTR szUrl,
  347. IInternetProtocolSink* pOIProtSink,
  348. IInternetBindInfo* pOIBindInfo,
  349. DWORD grfPI,
  350. HANDLE_PTR /* dwReserved */)
  351. {
  352. TRACELM(TRACE_DEBUG, "CDVDProt::Start()");
  353. if (!pOIProtSink)
  354. {
  355. TRACELM(TRACE_DEBUG, "CDVDProt::Start() IInternetProctocolSink * == NULL");
  356. return E_POINTER;
  357. }
  358. m_pSink.Release();
  359. m_pSink = pOIProtSink;
  360. m_pSink->ReportData(BSCF_FIRSTDATANOTIFICATION, 0, 0);
  361. #if 0
  362. // this bug is fixed in ie 5.5+ on whistler. if you want to run on earlier versions of ie such as 2k gold then you need this.
  363. m_pSink->ReportProgress(BINDSTATUS_CONNECTING, NULL); // put binding in downloading state so it doesn't ignore our IUnknown*
  364. #endif
  365. if (!pOIBindInfo) {
  366. m_pSink->ReportResult(E_NOINTERFACE, 0, 0);
  367. return E_NOINTERFACE;
  368. }
  369. // don't run unless we're being invoked from a safe site
  370. HRESULT hr = IsSafeSite(m_pSink);
  371. if (FAILED(hr)) {
  372. m_pSink->ReportResult(hr, 0, 0);
  373. return hr;
  374. }
  375. ULONG count;
  376. LPOLESTR pb;
  377. hr = pOIBindInfo->GetBindString(BINDSTRING_FLAG_BIND_TO_OBJECT, &pb, 1, &count);
  378. if (FAILED(hr)) {
  379. m_pSink->ReportResult(hr, 0, 0);
  380. return hr;
  381. }
  382. if (wcscmp(pb, BIND_TO_OBJ_VAL)) {
  383. // we must be getting a bind to storage so skip the expensive stuff and
  384. // wait for the bind to object which is coming next
  385. m_pSink->ReportData(BSCF_LASTDATANOTIFICATION |
  386. BSCF_DATAFULLYAVAILABLE, 0, 0);
  387. m_pSink->ReportResult(S_OK, 0, 0);
  388. m_pSink.Release();
  389. return S_OK;
  390. }
  391. // and, in one of the most bizarre maneuvers i've ever seen, rather than casting,
  392. // urlmon passes back the ascii value of the ibindctx pointer in the string
  393. hr = pOIBindInfo->GetBindString(BINDSTRING_PTR_BIND_CONTEXT, &pb, 1, &count);
  394. if (FAILED(hr)) {
  395. m_pSink->ReportResult(hr, 0, 0);
  396. return hr;
  397. }
  398. _ASSERT(count == 1);
  399. PQBindCtx pbindctx;
  400. #define RADIX_BASE_10 (10)
  401. #ifdef _WIN64
  402. #if 0
  403. // undone: turn this back on for win64 when _wcstoxi64 get into libc.c, they're in the header
  404. // but not implemented so this doesn't link
  405. pbindctx.Attach(reinterpret_cast<IBindCtx*>(_wcstoui64(pb, NULL, RADIX_BASE_10))); // urlmon already did an addref
  406. #else
  407. swscanf(pb, L"%I64d", &pbindctx.p);
  408. #endif // 0
  409. #else
  410. pbindctx.Attach(reinterpret_cast<IBindCtx*>(wcstol(pb, NULL, RADIX_BASE_10))); // urlmon already did an addref
  411. #endif // _WIN64
  412. if (!pbindctx) {
  413. m_pSink->ReportResult(E_NOINTERFACE, 0, 0);
  414. return E_NOINTERFACE;
  415. }
  416. TRACELM(TRACE_DEBUG, "CDVDProt::Start(): creating control object");
  417. PQVidCtl pCtl;
  418. PQWebBrowser2 pW2;
  419. // hunt for cached object
  420. PQServiceProvider pSP(m_pSink);
  421. if (pSP) {
  422. hr = pSP->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, (LPVOID *)&pW2);
  423. if (SUCCEEDED(hr)) {
  424. CComVariant v;
  425. CComBSTR propname(KEY_CLSID_VidCtl);
  426. if (!propname) {
  427. return E_UNEXPECTED;
  428. }
  429. hr = pW2->GetProperty(propname, &v);
  430. if (SUCCEEDED(hr)) {
  431. if (v.vt == VT_UNKNOWN) {
  432. pCtl = v.punkVal;
  433. } else if (v.vt == VT_DISPATCH) {
  434. pCtl = v.pdispVal;
  435. } else {
  436. TRACELM(TRACE_ERROR, "CDVDProt::Start(): non-object cached w/ our key");
  437. }
  438. // undone: look and see if pCtl already has a site.because
  439. // this means we're seeing the second tv: on this page
  440. // so just get the current TR/channel from it if necessary (tv: w/ no rhs)
  441. // and create a new ctl
  442. }
  443. }
  444. }
  445. if (!pCtl) {
  446. // undone: long term, we want to move a bunch of this create/setup logic into factoryhelp
  447. // so we can share more code with the dvd: protocol and the behavior factory
  448. hr = pCtl.CoCreateInstance(CLSID_MSVidCtl, NULL, CLSCTX_INPROC_SERVER);
  449. if (FAILED(hr)) {
  450. m_pSink->ReportResult(hr, 0, 0);
  451. return hr;
  452. }
  453. // cache this ctl for next time
  454. if (pW2) {
  455. VARIANT v;
  456. v.vt = VT_UNKNOWN;
  457. v.punkVal = pCtl;
  458. CComBSTR propname(KEY_CLSID_VidCtl);
  459. if (!propname) {
  460. return E_UNEXPECTED;
  461. }
  462. hr = pW2->PutProperty(propname, v);
  463. if (FAILED(hr)) {
  464. TRACELM(TRACE_ERROR, "CTVProt::Start() Can't cache ctl");
  465. }
  466. }
  467. // pass the url to view, it will be parsed in pCtrl->View()
  468. CComVariant vUrl(szUrl);
  469. hr = pCtl->View(&vUrl);
  470. if (FAILED(hr)) {
  471. m_pSink->ReportResult(hr, 0, 0);
  472. TRACELM(TRACE_ERROR, "CDVDProt::Start() Can't view dvd url");
  473. return hr;
  474. }
  475. // undone: once we know where vidctl will live in the registry then we need to put a flag
  476. // in the registry just disables including any features in the tv: prot
  477. // this must be secured admin only since its a backdoor to disable CA
  478. // undone: look up default feature segments in registry
  479. // for now we're just going to take them all since the
  480. // only one that exists is data
  481. PQFeatures pF;
  482. hr = pCtl->get_FeaturesAvailable(&pF);
  483. if (FAILED(hr)) {
  484. m_pSink->ReportResult(hr, 0, 0);
  485. TRACELM(TRACE_ERROR, "CDVDProt::Start() Can't get features collection");
  486. return hr;
  487. }
  488. // undone: look up default feature segments for dvd: in registry
  489. // for now we're just going to hard code the ones we want
  490. CFeatures* pC = static_cast<CFeatures *>(pF.p);
  491. CFeatures* pNewColl = new CFeatures;
  492. if (!pNewColl) {
  493. return E_OUTOFMEMORY;
  494. }
  495. for (DeviceCollection::iterator i = pC->m_Devices.begin(); i != pC->m_Devices.end(); ++i) {
  496. PQFeature f(*i);
  497. GUID2 clsid;
  498. hr = f->get__ClassID(&clsid);
  499. if (FAILED(hr)) {
  500. TRACELM(TRACE_ERROR, "CTVProt::GetVidCtl() Can't get feature class id");
  501. continue;
  502. }
  503. if (clsid == CLSID_MSVidClosedCaptioning) {
  504. pNewColl->m_Devices.push_back(*i);
  505. }
  506. }
  507. hr = pCtl->put_FeaturesActive(pNewColl);
  508. if (FAILED(hr)) {
  509. m_pSink->ReportResult(hr, 0, 0);
  510. TRACELM(TRACE_ERROR, "CDVDProt::Start() Can't put features collection");
  511. return hr;
  512. }
  513. }
  514. ASSERT(pCtl);
  515. hr = pbindctx->RegisterObjectParam(OLESTR("IUnknown Pointer"), pCtl);
  516. if (FAILED(hr)) {
  517. m_pSink->ReportResult(hr, 0, 0);
  518. return hr;
  519. }
  520. hr = pCtl->Run();
  521. if (FAILED(hr)) {
  522. m_pSink->ReportResult(hr, 0, 0);
  523. return hr;
  524. }
  525. TRACELSM(TRACE_DEBUG, (dbgDump << "BINDSTATUS_IUNKNOWNAVAILABLE(29), " << KEY_CLSID_VidCtl), "");
  526. m_pSink->ReportProgress(BINDSTATUS_IUNKNOWNAVAILABLE, NULL);
  527. m_pSink->ReportData(BSCF_LASTDATANOTIFICATION |
  528. BSCF_DATAFULLYAVAILABLE, 0, 0);
  529. m_pSink->ReportResult(S_OK, 0, 0);
  530. m_pSink.Release();
  531. return S_OK;
  532. }
  533. #endif // TUNING_MODEL_ONLY
  534. // end of file dvdprot.cpp