ContentProvider.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. /******************************************************************************
  2. |* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  3. |* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  4. |* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  5. |* PARTICULAR PURPOSE.
  6. |*
  7. |* Copyright 1995-2005 Nero AG and its licensors. All Rights Reserved.
  8. |*-----------------------------------------------------------------------------
  9. |* NeroSDK / NeroVisionAPI
  10. |*
  11. |* PROGRAM: ContentProvider.cpp
  12. |*
  13. |* PURPOSE: Test application for the NeroVision API: Sample content provider
  14. ******************************************************************************/
  15. #include "stdafx.h"
  16. #include "ContentResolver.h"
  17. #include <string>
  18. #include <math.h>
  19. using namespace NeroVisionAPI;
  20. #define STREAM_DURATION 600000000
  21. #ifndef PI
  22. #define PI 3.1415926535897932384626433832795
  23. #endif
  24. // Converts passed string into morse code. Returns a string representation of morse
  25. // code using characters '.', '-', ' '. For each unknown character a '?' is inserted into
  26. // the output string.
  27. static std::string morse(const char * in)
  28. {
  29. static unsigned char tab[] = {
  30. 0x3F, 0x3E, 0x3C, 0x38, 0x30, 0x20, 0x21, 0x23, 0x27, 0x2F, 0x00, 0x00, 0x00, 0x00,
  31. 0x00, 0x00, 0x00, 0x06, 0x11, 0x15, 0x09, 0x02, 0x14, 0x0B, 0x10, 0x04, 0x1E, 0x0D,
  32. 0x12, 0x07, 0x05, 0x0F, 0x16, 0x1B, 0x0A, 0x08, 0x03, 0x0C, 0x18, 0x0E, 0x19, 0x1D, 0x13
  33. };
  34. std::string out;
  35. while (*in) {
  36. if (out.size())
  37. out.append(1, ' ');
  38. unsigned int ch = toupper(*in);
  39. if (ch == ' ')
  40. out.append(" ");
  41. else if (ch >= '0' && ch <= 'Z') {
  42. unsigned char mc = tab[ch-'0'];
  43. while (mc & 0xFE) {
  44. out.append(1, mc & 1 ? '-' : '.');
  45. mc >>= 1;
  46. }
  47. }
  48. else
  49. out.append(1, '?');
  50. in++;
  51. }
  52. return out;
  53. }
  54. HRESULT GetInterface(LPUNKNOWN pUnk, void **ppv)
  55. {
  56. if (!ppv) return E_POINTER;
  57. *ppv = pUnk;
  58. pUnk->AddRef();
  59. return S_OK;
  60. }
  61. // implementation of IAVStreamSample
  62. class StreamSample: public BaseObj, public IAVStreamSample
  63. {
  64. public:
  65. IMPL_IUNKNOWN
  66. IMPL_INTERFACE(IAVStreamSample)
  67. StreamSample(const LONGLONG& start, const LONGLONG& stop, void* data, DWORD size): m_start(start), m_stop(stop), m_data(data), m_size(size) {}
  68. virtual ~StreamSample() { free(m_data); }
  69. STDMETHODIMP raw_GetTime(LONGLONG* start, LONGLONG* stop) { *start = m_start; *stop = m_stop; return S_OK; }
  70. STDMETHODIMP raw_GetData(DWORD* size, void** pData) { *size = m_size; *pData = m_data; return S_OK; }
  71. private:
  72. LONGLONG m_start, m_stop;
  73. void* m_data;
  74. DWORD m_size;
  75. };
  76. // base implementation of IAVStream, the classes VideoContentStream and AudioContentStream
  77. // are derived from this class
  78. class BaseContentStream: public BaseObj, public IAVStream
  79. {
  80. public:
  81. BaseContentStream(const std::wstring& name, const LONGLONG& len): m_len(len), m_name(name) {}
  82. IMPL_IUNKNOWN
  83. IMPL_INTERFACE(IAVStream)
  84. STDMETHODIMP raw_SetPosition(LONGLONG* pos) { m_pos = *pos; return S_OK; }
  85. STDMETHODIMP raw_GetSample(IAVStreamSample** sample);
  86. protected:
  87. virtual void* CreateSample(LONGLONG& pos, DWORD& size, LONGLONG& duration) = 0;
  88. LONGLONG m_pos;
  89. LONGLONG m_len;
  90. const std::wstring m_name;
  91. };
  92. // class implementing a video stream,
  93. // generated video frames will contain the stream name and the frame number
  94. class VideoContentStream: public BaseContentStream
  95. {
  96. public:
  97. VideoContentStream(const std::wstring& name, const VideoInfo& vi, const LONGLONG& len);
  98. virtual ~VideoContentStream();
  99. private:
  100. void* CreateSample(LONGLONG& pos, DWORD& size, LONGLONG& duration);
  101. VideoInfo m_videoInfo;
  102. HDC m_dc;
  103. HFONT m_font, m_oldFont;
  104. HBITMAP m_bmp, m_oldBmp;
  105. int m_imgSize;
  106. void* m_pixels;
  107. };
  108. // class implementing an audio stream,
  109. // generated audio stream will contain the stream name in morse code
  110. class AudioContentStream: public BaseContentStream
  111. {
  112. public:
  113. AudioContentStream(const std::wstring& name, const AudioInfo& ai, const LONGLONG& len);
  114. virtual ~AudioContentStream();
  115. private:
  116. void* CreateSample(LONGLONG& pos, DWORD& size, LONGLONG& duration);
  117. int CreateBeeps(const char* code, void* src, void* dst);
  118. void AddPause(int dits, unsigned char*& dst, int& len);
  119. void AddBeep(int dits, unsigned char*& dst, const void* src, int& len);
  120. AudioInfo m_audioInfo;
  121. int m_ditLen;
  122. unsigned char* m_sound;
  123. int m_seconds;
  124. };
  125. // provider for a video stream
  126. class VideoProvider: public BaseObj, public IVideoStreamProvider
  127. {
  128. public:
  129. VideoProvider(const std::wstring& name): m_name(name) {}
  130. IMPL_IUNKNOWN
  131. HRESULT InternalQueryInterface(REFIID riid, void** ppv)
  132. {
  133. if (riid == __uuidof(IContentProvider))
  134. return GetInterface(static_cast<IContentProvider*>(this), ppv);
  135. else if (riid == __uuidof(IAVStreamProvider))
  136. return GetInterface(static_cast<IAVStreamProvider*>(this), ppv);
  137. else if (riid == __uuidof(IVideoStreamProvider))
  138. return GetInterface(static_cast<IVideoStreamProvider*>(this), ppv);
  139. else
  140. return BaseObj::InternalQueryInterface(riid, ppv);
  141. }
  142. STDMETHODIMP raw_GetInfo(VideoInfo* info);
  143. STDMETHODIMP raw_GetStream(IAVStream** stream);
  144. STDMETHODIMP get_ProvidedContentType(ContentType* pCT) { *pCT = VideoStream; return S_OK; }
  145. STDMETHODIMP raw_GetDuration(hyper* dur) { *dur = STREAM_DURATION; return S_OK; }
  146. private:
  147. const std::wstring m_name;
  148. };
  149. // provider for an audio stream
  150. class AudioProvider: public BaseObj, public IAudioStreamProvider
  151. {
  152. public:
  153. AudioProvider(const std::wstring& name): m_name(name) {}
  154. IMPL_IUNKNOWN
  155. HRESULT InternalQueryInterface(REFIID riid, void** ppv)
  156. {
  157. if (riid == __uuidof(IContentProvider))
  158. return GetInterface(static_cast<IContentProvider*>(this), ppv);
  159. else if (riid == __uuidof(IAVStreamProvider))
  160. return GetInterface(static_cast<IAVStreamProvider*>(this), ppv);
  161. else if (riid == __uuidof(IAudioStreamProvider))
  162. return GetInterface(static_cast<IAudioStreamProvider*>(this), ppv);
  163. else
  164. return BaseObj::InternalQueryInterface(riid, ppv);
  165. }
  166. STDMETHODIMP raw_GetInfo(AudioInfo* info);
  167. STDMETHODIMP raw_GetStream(IAVStream** stream);
  168. STDMETHODIMP get_ProvidedContentType(ContentType* pCT) { *pCT = AudioStream; return S_OK; }
  169. STDMETHODIMP raw_GetDuration(hyper* dur) { *dur = STREAM_DURATION; return S_OK; }
  170. private:
  171. const std::wstring m_name;
  172. };
  173. // ID resolving of content provider: for this test application any ID is valid. In case
  174. // the ID begins with "A:", the method returns an audio provider, all other IDs resolve
  175. // to a video provider, empty IDs cause an error
  176. HRESULT ContentResolver::raw_ResolveContent(BSTR id, IContentProvider** ppCP)
  177. {
  178. std::wstring str(id);
  179. if (str.length()) {
  180. if (str.substr(0, 2).compare(L"A:") == 0) {
  181. *ppCP = new AudioProvider(str.substr(2));
  182. return S_OK;
  183. }
  184. else if (str.substr(0, 2).compare(L"V:") == 0) {
  185. *ppCP = new VideoProvider(str.substr(2));
  186. return S_OK;
  187. }
  188. else {
  189. *ppCP = new VideoProvider(str);
  190. return S_OK;
  191. }
  192. }
  193. return E_INVALIDARG;
  194. }
  195. // provided test stream has the following properties: RGB32, 25 fps, aspect 4:3,
  196. // progressive, video size defaults to 480x360 but can be changed for testing
  197. // purpose by specifying the desired size at the beginning of the stream name
  198. HRESULT VideoProvider::raw_GetInfo(VideoInfo* info)
  199. {
  200. // if possible take width and height from stream name
  201. if (swscanf(m_name.c_str(), L"%d x %d", &info->viWidth, &info->viHeight) == 2) {
  202. info->viWidth = max(1, info->viWidth);
  203. info->viHeight = max(1, info->viHeight);
  204. }
  205. else {
  206. info->viWidth = 480;
  207. info->viHeight = 360;
  208. }
  209. info->viFormat = VideoFormat_RGB32;
  210. info->viStructure = FrameStructure_Progressive;
  211. info->viTimePerFrame = 400000;
  212. info->viAspectX = 4;
  213. info->viAspectY = 3;
  214. return S_OK;
  215. }
  216. // provided test stream is 44.1kHz, 16 bit, mono
  217. HRESULT AudioProvider::raw_GetInfo(AudioInfo* info)
  218. {
  219. info->aiFormat = AudioFormat_PCM;
  220. info->aiSamplesPerSec = 44100;
  221. info->aiBitsPerSample = 16;
  222. info->aiNumChannels = 1;
  223. return S_OK;
  224. }
  225. HRESULT VideoProvider::raw_GetStream(IAVStream** stream)
  226. {
  227. VideoInfo vi;
  228. LONGLONG d;
  229. raw_GetInfo(&vi);
  230. raw_GetDuration(&d);
  231. *stream = new VideoContentStream(m_name, vi, d);
  232. return S_OK;
  233. }
  234. HRESULT AudioProvider::raw_GetStream(IAVStream** stream)
  235. {
  236. AudioInfo ai;
  237. LONGLONG d;
  238. raw_GetInfo(&ai);
  239. raw_GetDuration(&d);
  240. *stream = new AudioContentStream(m_name, ai, d);
  241. return S_OK;
  242. }
  243. // creates a StreamSample object for each sample in the stream,
  244. // S_FALSE is returned to signal "end of stream",
  245. HRESULT BaseContentStream::raw_GetSample(IAVStreamSample** sample)
  246. {
  247. if (m_pos >= m_len)
  248. return S_FALSE;
  249. // base class uses CreateSample to generate the actual sample,
  250. // this method is implemented by derived classes
  251. DWORD size;
  252. LONGLONG dur;
  253. void* data = CreateSample(m_pos, size, dur);
  254. *sample = new StreamSample(m_pos, m_pos + dur, data, size);
  255. m_pos += dur;
  256. return S_OK;
  257. }
  258. VideoContentStream::VideoContentStream(const std::wstring& name, const VideoInfo& vi, const LONGLONG& len):
  259. BaseContentStream(name, len), m_videoInfo(vi)
  260. {
  261. // this class uses Windows GDI for drawing text into video frames,
  262. // initialize device context, font and bitmap
  263. m_dc = CreateCompatibleDC(0);
  264. int bpp = m_videoInfo.viFormat == VideoFormat_RGB24 ? 24 : 32;
  265. m_imgSize = m_videoInfo.viHeight * ((m_videoInfo.viWidth * bpp / 8 + 3) & ~3);
  266. LOGFONT lf;
  267. lf.lfHeight = m_videoInfo.viHeight / 16;
  268. lf.lfWidth = 0;
  269. lf.lfEscapement = 0;
  270. lf.lfOrientation = 0;
  271. lf.lfWeight = FW_DONTCARE;
  272. lf.lfItalic = 0;
  273. lf.lfUnderline = 0;
  274. lf.lfStrikeOut = 0;
  275. lf.lfCharSet = DEFAULT_CHARSET;
  276. lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
  277. lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  278. lf.lfQuality = ANTIALIASED_QUALITY;
  279. lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
  280. _tcscpy(lf.lfFaceName, TEXT("Arial"));
  281. HFONT smallFont = CreateFontIndirect(&lf);
  282. m_oldFont = (HFONT)SelectObject(m_dc, smallFont);
  283. BITMAPINFO bi = {0};
  284. bi.bmiHeader.biBitCount = bpp;
  285. bi.bmiHeader.biCompression = BI_RGB;
  286. bi.bmiHeader.biWidth = m_videoInfo.viWidth;
  287. bi.bmiHeader.biHeight = m_videoInfo.viHeight;
  288. bi.bmiHeader.biPlanes = 1;
  289. bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  290. bi.bmiHeader.biSizeImage = m_imgSize;
  291. m_bmp = CreateDIBSection(m_dc, &bi, DIB_RGB_COLORS, &m_pixels, 0, 0);
  292. m_oldBmp = (HBITMAP)SelectObject(m_dc, m_bmp);
  293. SetBkColor(m_dc, RGB(0, 0, 0));
  294. SetTextColor(m_dc, RGB(210, 210, 0));
  295. RECT rect = {0, m_videoInfo.viHeight * 14 / 16, m_videoInfo.viWidth, m_videoInfo.viHeight};
  296. DrawText(m_dc, _bstr_t(m_name.c_str()), -1, &rect, DT_CENTER | DT_TOP);
  297. lf.lfHeight = m_videoInfo.viHeight / 2;
  298. m_font = CreateFontIndirect(&lf);
  299. SelectObject(m_dc, m_font);
  300. DeleteObject(smallFont);
  301. }
  302. VideoContentStream::~VideoContentStream()
  303. {
  304. SelectObject(m_dc, m_oldFont);
  305. SelectObject(m_dc, m_oldBmp);
  306. DeleteObject(m_font);
  307. DeleteObject(m_bmp);
  308. DeleteDC(m_dc);
  309. }
  310. void* VideoContentStream::CreateSample(LONGLONG& pos, DWORD& size, LONGLONG& duration)
  311. {
  312. void* p = malloc(m_imgSize);
  313. duration = 400000;
  314. // get integer frame number
  315. int n = int(pos / duration);
  316. pos = n * duration;
  317. // clear image and draw number
  318. BitBlt(m_dc, 0, 0, m_videoInfo.viWidth, m_videoInfo.viHeight * 14 / 16, m_dc, 0, 0, BLACKNESS);
  319. TCHAR txt[5];
  320. _stprintf(txt, _T("%d"), n % 10000);
  321. RECT rect = {0, 0, m_videoInfo.viWidth, m_videoInfo.viHeight};
  322. DrawText(m_dc, txt, -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
  323. // copy into output buffer
  324. memcpy(p, m_pixels, m_imgSize);
  325. return p;
  326. }
  327. AudioContentStream::AudioContentStream(const std::wstring& name, const AudioInfo& ai, const LONGLONG& len):
  328. BaseContentStream(name, len), m_audioInfo(ai)
  329. {
  330. const double freq = 2000;
  331. const int bytePerSec = m_audioInfo.aiSamplesPerSec * m_audioInfo.aiNumChannels * m_audioInfo.aiBitsPerSample / 8;
  332. int ditSamples = 0.06 * m_audioInfo.aiSamplesPerSec;
  333. m_ditLen = m_audioInfo.aiNumChannels * m_audioInfo.aiBitsPerSample / 8 * ditSamples;
  334. // generate sine wave to use as beep for morse code
  335. void* sine = malloc(3 * m_ditLen);
  336. short* ptr = (short*)sine;
  337. for (int sample = 0; sample < 3 * ditSamples; ++sample) {
  338. int value = 24000 * sin(freq * 2 * PI * sample / m_audioInfo.aiSamplesPerSec);
  339. for (int ch = 0; ch < m_audioInfo.aiNumChannels; ++ch) {
  340. *ptr++ = value;
  341. }
  342. }
  343. // convert stream name to mose code string
  344. std::string s = morse(_bstr_t(m_name.c_str()));
  345. // generate audio from morse code string,
  346. // call CreateBeeps once to determine required buffer size
  347. int l = CreateBeeps(s.c_str(), 0, 0);
  348. // add 5 sec pause at end and round to whole seconds
  349. m_seconds = l / double(bytePerSec) + 5.5;
  350. l = m_seconds * bytePerSec;
  351. m_sound = (unsigned char*)malloc(l);
  352. memset(m_sound, 0, l);
  353. // audio signal is generated once and saved in a buffer,
  354. // later each stream sample will get its audio from this buffer
  355. CreateBeeps(s.c_str(), sine, m_sound);
  356. free(sine);
  357. }
  358. void AudioContentStream::AddPause(int dits, unsigned char*& dst, int& len)
  359. {
  360. int l = dits * m_ditLen;
  361. if (dst) {
  362. memset(dst, 0, l);
  363. dst += l;
  364. }
  365. len += l;
  366. }
  367. void AudioContentStream::AddBeep(int dits, unsigned char*& dst, const void* src, int& len)
  368. {
  369. int l = dits * m_ditLen;
  370. if (dst) {
  371. memcpy(dst, src, l);
  372. dst += l;
  373. }
  374. len += l;
  375. }
  376. // convert morse code string into audio signal
  377. int AudioContentStream::CreateBeeps(const char* code, void* src, void* dst)
  378. {
  379. int len = 0;
  380. unsigned char* b = (unsigned char*) dst;
  381. while (*code) {
  382. switch (*code) {
  383. case '.':
  384. AddBeep(1, b, src, len);
  385. AddPause(1, b, len);
  386. break;
  387. case '-':
  388. AddBeep(3, b, src, len);
  389. AddPause(1, b, len);
  390. break;
  391. default:
  392. AddPause(6, b, len);
  393. break;
  394. }
  395. ++code;
  396. }
  397. return len;
  398. }
  399. AudioContentStream::~AudioContentStream()
  400. {
  401. free(m_sound);
  402. }
  403. void* AudioContentStream::CreateSample(LONGLONG& pos, DWORD& size, LONGLONG& duration)
  404. {
  405. // deliver 1/4 sec of audio per sample,
  406. // adjust pos to integer multiple of quarter seconds
  407. const int bytePerSec = m_audioInfo.aiSamplesPerSec * m_audioInfo.aiNumChannels * m_audioInfo.aiBitsPerSample / 8;
  408. int q = pos / 2500000;
  409. pos = LONGLONG(q) * 2500000;
  410. size = bytePerSec / 4;
  411. void* p = malloc(size);
  412. duration = 2500000;
  413. // copy the required audio part from our buffer into the sample
  414. // (audio restarts from beginning if stream is longer than morse code buffer)
  415. memcpy(p, m_sound + (q % (4 * m_seconds)) * size, size);
  416. return p;
  417. }