captions-mssapi-stream.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. #include "captions-mssapi-stream.hpp"
  2. #include "captions-mssapi.hpp"
  3. #include <mmreg.h>
  4. #include <util/windows/CoTaskMemPtr.hpp>
  5. #include <util/threading.h>
  6. #include <util/base.h>
  7. using namespace std;
  8. #if 0
  9. #define debugfunc(format, ...) blog(LOG_DEBUG, "[Captions] %s(" format ")", __FUNCTION__, ##__VA_ARGS__)
  10. #else
  11. #define debugfunc(format, ...)
  12. #endif
  13. CaptionStream::CaptionStream(DWORD samplerate_, mssapi_captions *handler_)
  14. : handler(handler_),
  15. samplerate(samplerate_),
  16. event(CreateEvent(nullptr, false, false, nullptr))
  17. {
  18. buf_info.ulMsMinNotification = 50;
  19. buf_info.ulMsBufferSize = 500;
  20. buf_info.ulMsEventBias = 0;
  21. format.wFormatTag = WAVE_FORMAT_PCM;
  22. format.nChannels = 1;
  23. format.nSamplesPerSec = 16000;
  24. format.nAvgBytesPerSec = format.nSamplesPerSec * sizeof(uint16_t);
  25. format.nBlockAlign = 2;
  26. format.wBitsPerSample = 16;
  27. format.cbSize = sizeof(format);
  28. }
  29. void CaptionStream::Stop()
  30. {
  31. {
  32. lock_guard<mutex> lock(m);
  33. deque_free(buf);
  34. }
  35. cv.notify_one();
  36. }
  37. void CaptionStream::PushAudio(const void *data, size_t frames)
  38. {
  39. bool ready = false;
  40. lock_guard<mutex> lock(m);
  41. deque_push_back(buf, data, frames * sizeof(int16_t));
  42. write_pos += frames * sizeof(int16_t);
  43. if (wait_size && buf->size >= wait_size)
  44. ready = true;
  45. if (ready)
  46. cv.notify_one();
  47. }
  48. // IUnknown methods
  49. STDMETHODIMP CaptionStream::QueryInterface(REFIID riid, void **ppv)
  50. {
  51. if (riid == IID_IUnknown) {
  52. AddRef();
  53. *ppv = this;
  54. } else if (riid == IID_IStream) {
  55. AddRef();
  56. *ppv = (IStream *)this;
  57. } else if (riid == IID_ISpStreamFormat) {
  58. AddRef();
  59. *ppv = (ISpStreamFormat *)this;
  60. } else if (riid == IID_ISpAudio) {
  61. AddRef();
  62. *ppv = (ISpAudio *)this;
  63. } else {
  64. *ppv = nullptr;
  65. return E_NOINTERFACE;
  66. }
  67. return NOERROR;
  68. }
  69. STDMETHODIMP_(ULONG) CaptionStream::AddRef()
  70. {
  71. return (ULONG)os_atomic_inc_long(&refs);
  72. }
  73. STDMETHODIMP_(ULONG) CaptionStream::Release()
  74. {
  75. ULONG new_refs = (ULONG)os_atomic_dec_long(&refs);
  76. if (!new_refs)
  77. delete this;
  78. return new_refs;
  79. }
  80. // ISequentialStream methods
  81. STDMETHODIMP CaptionStream::Read(void *data, ULONG bytes, ULONG *read_bytes)
  82. {
  83. HRESULT hr = S_OK;
  84. size_t cur_size;
  85. debugfunc("data, %lu, read_bytes", bytes);
  86. if (!data)
  87. return STG_E_INVALIDPOINTER;
  88. {
  89. lock_guard<mutex> lock1(m);
  90. wait_size = bytes;
  91. cur_size = buf->size;
  92. }
  93. unique_lock<mutex> lock(m);
  94. if (bytes > cur_size)
  95. cv.wait(lock);
  96. if (bytes > (ULONG)buf->size) {
  97. bytes = (ULONG)buf->size;
  98. hr = S_FALSE;
  99. }
  100. if (bytes)
  101. deque_pop_front(buf, data, bytes);
  102. if (read_bytes)
  103. *read_bytes = bytes;
  104. wait_size = 0;
  105. pos.QuadPart += bytes;
  106. return hr;
  107. }
  108. STDMETHODIMP CaptionStream::Write(const void *, ULONG bytes, ULONG *)
  109. {
  110. debugfunc("data, %lu, written_bytes", bytes);
  111. return STG_E_INVALIDFUNCTION;
  112. }
  113. // IStream methods
  114. STDMETHODIMP CaptionStream::Seek(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *new_pos)
  115. {
  116. debugfunc("%lld, %lx, new_pos", move, origin);
  117. if (!new_pos)
  118. return E_POINTER;
  119. if (origin != SEEK_CUR || move.QuadPart != 0)
  120. return E_NOTIMPL;
  121. *new_pos = pos;
  122. return S_OK;
  123. }
  124. STDMETHODIMP CaptionStream::SetSize(ULARGE_INTEGER new_size)
  125. {
  126. debugfunc("%llu", new_size);
  127. return STG_E_INVALIDFUNCTION;
  128. }
  129. STDMETHODIMP CaptionStream::CopyTo(IStream *stream, ULARGE_INTEGER bytes, ULARGE_INTEGER *read_bytes,
  130. ULARGE_INTEGER *written_bytes)
  131. {
  132. HRESULT hr;
  133. debugfunc("stream, %llu, read_bytes, written_bytes", bytes);
  134. if (!stream)
  135. return STG_E_INVALIDPOINTER;
  136. ULONG written = 0;
  137. if (bytes.QuadPart > (ULONGLONG)buf->size)
  138. bytes.QuadPart = (ULONGLONG)buf->size;
  139. lock_guard<mutex> lock(m);
  140. temp_buf.resize((size_t)bytes.QuadPart);
  141. deque_peek_front(buf, &temp_buf[0], (size_t)bytes.QuadPart);
  142. hr = stream->Write(temp_buf.data(), (ULONG)bytes.QuadPart, &written);
  143. if (read_bytes)
  144. *read_bytes = bytes;
  145. if (written_bytes)
  146. written_bytes->QuadPart = written;
  147. return hr;
  148. }
  149. STDMETHODIMP CaptionStream::Commit(DWORD commit_flags)
  150. {
  151. debugfunc("%lx", commit_flags);
  152. /* TODO? */
  153. return S_OK;
  154. }
  155. STDMETHODIMP CaptionStream::Revert(void)
  156. {
  157. debugfunc("");
  158. return S_OK;
  159. }
  160. STDMETHODIMP CaptionStream::LockRegion(ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type)
  161. {
  162. debugfunc("%llu, %llu, %ld", offset, size, type);
  163. /* TODO? */
  164. return STG_E_INVALIDFUNCTION;
  165. }
  166. STDMETHODIMP CaptionStream::UnlockRegion(ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type)
  167. {
  168. debugfunc("%llu, %llu, %ld", offset, size, type);
  169. /* TODO? */
  170. return STG_E_INVALIDFUNCTION;
  171. }
  172. static const wchar_t *stat_name = L"Caption stream";
  173. STDMETHODIMP CaptionStream::Stat(STATSTG *stg, DWORD flag)
  174. {
  175. debugfunc("stg, %lu", flag);
  176. if (!stg)
  177. return E_POINTER;
  178. lock_guard<mutex> lock(m);
  179. *stg = {};
  180. stg->type = STGTY_STREAM;
  181. stg->cbSize.QuadPart = (ULONGLONG)buf->size;
  182. if (flag == STATFLAG_DEFAULT) {
  183. size_t byte_size = (wcslen(stat_name) + 1) * sizeof(wchar_t);
  184. stg->pwcsName = (wchar_t *)CoTaskMemAlloc(byte_size);
  185. memcpy(stg->pwcsName, stat_name, byte_size);
  186. }
  187. return S_OK;
  188. }
  189. STDMETHODIMP CaptionStream::Clone(IStream **stream)
  190. {
  191. debugfunc("stream");
  192. *stream = nullptr;
  193. return E_NOTIMPL;
  194. }
  195. // ISpStreamFormat methods
  196. STDMETHODIMP CaptionStream::GetFormat(GUID *guid, WAVEFORMATEX **co_mem_wfex_out)
  197. {
  198. debugfunc("guid, co_mem_wfex_out");
  199. if (!guid || !co_mem_wfex_out)
  200. return E_POINTER;
  201. if (format.wFormatTag == 0) {
  202. *co_mem_wfex_out = nullptr;
  203. return S_OK;
  204. }
  205. void *wfex = CoTaskMemAlloc(sizeof(format));
  206. memcpy(wfex, &format, sizeof(format));
  207. *co_mem_wfex_out = (WAVEFORMATEX *)wfex;
  208. return S_OK;
  209. }
  210. // ISpAudio methods
  211. STDMETHODIMP CaptionStream::SetState(SPAUDIOSTATE state_, ULONGLONG)
  212. {
  213. debugfunc("%lu, reserved", state_);
  214. state = state_;
  215. return S_OK;
  216. }
  217. STDMETHODIMP CaptionStream::SetFormat(REFGUID guid_ref, const WAVEFORMATEX *wfex)
  218. {
  219. debugfunc("guid, wfex");
  220. if (!wfex)
  221. return E_INVALIDARG;
  222. if (guid_ref == SPDFID_WaveFormatEx) {
  223. lock_guard<mutex> lock(m);
  224. memcpy(&format, wfex, sizeof(format));
  225. if (!handler->reset_resampler(AUDIO_FORMAT_16BIT, wfex->nSamplesPerSec))
  226. return E_FAIL;
  227. /* 50 msec */
  228. DWORD size = format.nSamplesPerSec / 20;
  229. DWORD byte_size = size * format.nBlockAlign;
  230. deque_reserve(buf, (size_t)byte_size);
  231. }
  232. return S_OK;
  233. }
  234. STDMETHODIMP CaptionStream::GetStatus(SPAUDIOSTATUS *status)
  235. {
  236. debugfunc("status");
  237. if (!status)
  238. return E_POINTER;
  239. /* TODO? */
  240. lock_guard<mutex> lock(m);
  241. *status = {};
  242. status->cbNonBlockingIO = (ULONG)buf->size;
  243. status->State = state;
  244. status->CurSeekPos = pos.QuadPart;
  245. status->CurDevicePos = write_pos;
  246. return S_OK;
  247. }
  248. STDMETHODIMP CaptionStream::SetBufferInfo(const SPAUDIOBUFFERINFO *buf_info_)
  249. {
  250. debugfunc("buf_info");
  251. /* TODO */
  252. buf_info = *buf_info_;
  253. return S_OK;
  254. }
  255. STDMETHODIMP CaptionStream::GetBufferInfo(SPAUDIOBUFFERINFO *buf_info_)
  256. {
  257. debugfunc("buf_info");
  258. if (!buf_info_)
  259. return E_POINTER;
  260. *buf_info_ = buf_info;
  261. return S_OK;
  262. }
  263. STDMETHODIMP CaptionStream::GetDefaultFormat(GUID *format, WAVEFORMATEX **co_mem_wfex_out)
  264. {
  265. debugfunc("format, co_mem_wfex_out");
  266. if (!format || !co_mem_wfex_out)
  267. return E_POINTER;
  268. void *wfex = CoTaskMemAlloc(sizeof(format));
  269. memcpy(wfex, &format, sizeof(format));
  270. *format = SPDFID_WaveFormatEx;
  271. *co_mem_wfex_out = (WAVEFORMATEX *)wfex;
  272. return S_OK;
  273. }
  274. STDMETHODIMP_(HANDLE) CaptionStream::EventHandle(void)
  275. {
  276. debugfunc("");
  277. return event;
  278. }
  279. STDMETHODIMP CaptionStream::GetVolumeLevel(ULONG *level)
  280. {
  281. debugfunc("level");
  282. if (!level)
  283. return E_POINTER;
  284. *level = vol;
  285. return S_OK;
  286. }
  287. STDMETHODIMP CaptionStream::SetVolumeLevel(ULONG level)
  288. {
  289. debugfunc("%lu", level);
  290. vol = level;
  291. return S_OK;
  292. }
  293. STDMETHODIMP CaptionStream::GetBufferNotifySize(ULONG *size)
  294. {
  295. debugfunc("size");
  296. if (!size)
  297. return E_POINTER;
  298. *size = notify_size;
  299. return S_OK;
  300. }
  301. STDMETHODIMP CaptionStream::SetBufferNotifySize(ULONG size)
  302. {
  303. debugfunc("%lu", size);
  304. notify_size = size;
  305. return S_OK;
  306. }