include: Avoid using attributes on elaborated type specifier.
[wine.git] / dlls / mp3dmod / mp3dmod.c
blobc9da2b8a3544bec5a522a6e60b0428cdd221c29c
1 /*
2 * MP3 decoder DMO
4 * Copyright 2018 Zebediah Figura
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
22 #include <stdio.h>
23 #include <mpg123.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "wingdi.h"
27 #include "mmreg.h"
28 #define COBJMACROS
29 #include "objbase.h"
30 #include "dmo.h"
31 #include "rpcproxy.h"
32 #include "wmcodecdsp.h"
33 #include "wine/debug.h"
34 #include "wine/heap.h"
36 #include "initguid.h"
37 DEFINE_GUID(WMMEDIATYPE_Audio, 0x73647561,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
38 DEFINE_GUID(WMMEDIASUBTYPE_MP3,0x00000055,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
39 DEFINE_GUID(WMMEDIASUBTYPE_PCM,0x00000001,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
41 WINE_DEFAULT_DEBUG_CHANNEL(mp3dmod);
43 static HINSTANCE mp3dmod_instance;
45 struct mp3_decoder {
46 IUnknown IUnknown_inner;
47 IMediaObject IMediaObject_iface;
48 IUnknown *outer;
49 LONG ref;
50 mpg123_handle *mh;
51 DMO_MEDIA_TYPE outtype;
52 IMediaBuffer *buffer;
53 REFERENCE_TIME timestamp;
56 static inline struct mp3_decoder *impl_from_IUnknown(IUnknown *iface)
58 return CONTAINING_RECORD(iface, struct mp3_decoder, IUnknown_inner);
61 static HRESULT WINAPI Unknown_QueryInterface(IUnknown *iface, REFIID iid, void **obj)
63 struct mp3_decoder *This = impl_from_IUnknown(iface);
65 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(iid), obj);
67 if (IsEqualGUID(iid, &IID_IUnknown))
68 *obj = &This->IUnknown_inner;
69 else if (IsEqualGUID(iid, &IID_IMediaObject))
70 *obj = &This->IMediaObject_iface;
71 else
73 FIXME("no interface for %s\n", debugstr_guid(iid));
74 *obj = NULL;
75 return E_NOINTERFACE;
78 IUnknown_AddRef((IUnknown *)*obj);
79 return S_OK;
82 static ULONG WINAPI Unknown_AddRef(IUnknown *iface)
84 struct mp3_decoder *This = impl_from_IUnknown(iface);
85 ULONG refcount = InterlockedIncrement(&This->ref);
87 TRACE("(%p) AddRef from %d\n", This, refcount - 1);
89 return refcount;
92 static ULONG WINAPI Unknown_Release(IUnknown *iface)
94 struct mp3_decoder *This = impl_from_IUnknown(iface);
95 ULONG refcount = InterlockedDecrement(&This->ref);
97 TRACE("(%p) Release from %d\n", This, refcount + 1);
99 if (!refcount)
101 MoFreeMediaType(&This->outtype);
102 mpg123_delete(This->mh);
103 heap_free(This);
105 return refcount;
108 static const IUnknownVtbl Unknown_vtbl = {
109 Unknown_QueryInterface,
110 Unknown_AddRef,
111 Unknown_Release,
114 static inline struct mp3_decoder *impl_from_IMediaObject(IMediaObject *iface)
116 return CONTAINING_RECORD(iface, struct mp3_decoder, IMediaObject_iface);
119 static HRESULT WINAPI MediaObject_QueryInterface(IMediaObject *iface, REFIID iid, void **obj)
121 struct mp3_decoder *This = impl_from_IMediaObject(iface);
122 return IUnknown_QueryInterface(This->outer, iid, obj);
125 static ULONG WINAPI MediaObject_AddRef(IMediaObject *iface)
127 struct mp3_decoder *This = impl_from_IMediaObject(iface);
128 return IUnknown_AddRef(This->outer);
131 static ULONG WINAPI MediaObject_Release(IMediaObject *iface)
133 struct mp3_decoder *This = impl_from_IMediaObject(iface);
134 return IUnknown_Release(This->outer);
137 static HRESULT WINAPI MediaObject_GetStreamCount(IMediaObject *iface, DWORD *input, DWORD *output)
139 FIXME("(%p)->(%p, %p) stub!\n", iface, input, output);
141 return E_NOTIMPL;
144 static HRESULT WINAPI MediaObject_GetInputStreamInfo(IMediaObject *iface, DWORD index, DWORD *flags)
146 FIXME("(%p)->(%d, %p) stub!\n", iface, index, flags);
148 return E_NOTIMPL;
151 static HRESULT WINAPI MediaObject_GetOutputStreamInfo(IMediaObject *iface, DWORD index, DWORD *flags)
153 FIXME("(%p)->(%d, %p) stub!\n", iface, index, flags);
155 return E_NOTIMPL;
158 static HRESULT WINAPI MediaObject_GetInputType(IMediaObject *iface, DWORD index, DWORD type_index, DMO_MEDIA_TYPE *type)
160 FIXME("(%p)->(%d, %d, %p) stub!\n", iface, index, type_index, type);
162 return E_NOTIMPL;
165 static HRESULT WINAPI MediaObject_GetOutputType(IMediaObject *iface, DWORD index, DWORD type_index, DMO_MEDIA_TYPE *type)
167 FIXME("(%p)->(%d, %d, %p) stub!\n", iface, index, type_index, type);
169 return E_NOTIMPL;
172 static HRESULT WINAPI MediaObject_SetInputType(IMediaObject *iface, DWORD index, const DMO_MEDIA_TYPE *type, DWORD flags)
174 FIXME("(%p)->(%d, %p, %#x) stub!\n", iface, index, type, flags);
176 return S_OK;
179 static HRESULT WINAPI MediaObject_SetOutputType(IMediaObject *iface, DWORD index, const DMO_MEDIA_TYPE *type, DWORD flags)
181 struct mp3_decoder *This = impl_from_IMediaObject(iface);
182 WAVEFORMATEX *format;
183 long enc;
184 int err;
186 TRACE("(%p)->(%d, %p, %#x)\n", iface, index, type, flags);
188 if (flags & DMO_SET_TYPEF_CLEAR)
190 MoFreeMediaType(&This->outtype);
191 return S_OK;
194 format = (WAVEFORMATEX *)type->pbFormat;
196 if (format->wBitsPerSample == 8)
197 enc = MPG123_ENC_UNSIGNED_8;
198 else if (format->wBitsPerSample == 16)
199 enc = MPG123_ENC_SIGNED_16;
200 else
202 ERR("Cannot decode to bit depth %u.\n", format->wBitsPerSample);
203 return DMO_E_TYPE_NOT_ACCEPTED;
206 if (!(flags & DMO_SET_TYPEF_TEST_ONLY))
208 err = mpg123_format(This->mh, format->nSamplesPerSec, format->nChannels, enc);
209 if (err != MPG123_OK)
211 ERR("Failed to set format: %u channels, %u samples/sec, %u bits/sample.\n",
212 format->nChannels, format->nSamplesPerSec, format->wBitsPerSample);
213 return DMO_E_TYPE_NOT_ACCEPTED;
215 MoCopyMediaType(&This->outtype, type);
218 return S_OK;
221 static HRESULT WINAPI MediaObject_GetInputCurrentType(IMediaObject *iface, DWORD index, DMO_MEDIA_TYPE *type)
223 FIXME("(%p)->(%d, %p) stub!\n", iface, index, type);
225 return E_NOTIMPL;
228 static HRESULT WINAPI MediaObject_GetOutputCurrentType(IMediaObject *iface, DWORD index, DMO_MEDIA_TYPE *type)
230 FIXME("(%p)->(%d, %p) stub!\n", iface, index, type);
232 return E_NOTIMPL;
235 static HRESULT WINAPI MediaObject_GetInputSizeInfo(IMediaObject *iface, DWORD index, DWORD *size, DWORD *max_lookahead, DWORD *alignment)
237 FIXME("(%p)->(%d, %p, %p, %p) stub!\n", iface, index, size, max_lookahead, alignment);
239 return E_NOTIMPL;
242 static HRESULT WINAPI MediaObject_GetOutputSizeInfo(IMediaObject *iface, DWORD index, DWORD *size, DWORD *alignment)
244 FIXME("(%p)->(%d, %p, %p) stub!\n", iface, index, size, alignment);
246 return E_NOTIMPL;
249 static HRESULT WINAPI MediaObject_GetInputMaxLatency(IMediaObject *iface, DWORD index, REFERENCE_TIME *latency)
251 FIXME("(%p)->(%d, %p) stub!\n", iface, index, latency);
253 return E_NOTIMPL;
256 static HRESULT WINAPI MediaObject_SetInputMaxLatency(IMediaObject *iface, DWORD index, REFERENCE_TIME latency)
258 FIXME("(%p)->(%d, %s) stub!\n", iface, index, wine_dbgstr_longlong(latency));
260 return E_NOTIMPL;
263 static HRESULT WINAPI MediaObject_Flush(IMediaObject *iface)
265 FIXME("(%p)->() stub!\n", iface);
267 return E_NOTIMPL;
270 static HRESULT WINAPI MediaObject_Discontinuity(IMediaObject *iface, DWORD index)
272 FIXME("(%p)->(%d) stub!\n", iface, index);
274 return E_NOTIMPL;
277 static HRESULT WINAPI MediaObject_AllocateStreamingResources(IMediaObject *iface)
279 FIXME("(%p)->() stub!\n", iface);
281 return E_NOTIMPL;
284 static HRESULT WINAPI MediaObject_FreeStreamingResources(IMediaObject *iface)
286 FIXME("(%p)->() stub!\n", iface);
288 return E_NOTIMPL;
291 static HRESULT WINAPI MediaObject_GetInputStatus(IMediaObject *iface, DWORD index, DWORD *flags)
293 FIXME("(%p)->(%d, %p) stub!\n", iface, index, flags);
295 return E_NOTIMPL;
298 static HRESULT WINAPI MediaObject_ProcessInput(IMediaObject *iface, DWORD index,
299 IMediaBuffer *buffer, DWORD flags, REFERENCE_TIME timestamp, REFERENCE_TIME timelength)
301 struct mp3_decoder *This = impl_from_IMediaObject(iface);
302 HRESULT hr;
303 BYTE *data;
304 DWORD len;
305 int err;
307 TRACE("(%p)->(%d, %p, %#x, %s, %s)\n", iface, index, buffer, flags,
308 wine_dbgstr_longlong(timestamp), wine_dbgstr_longlong(timelength));
310 if (This->buffer)
312 ERR("Already have a buffer.\n");
313 return DMO_E_NOTACCEPTING;
316 IMediaBuffer_AddRef(buffer);
317 This->buffer = buffer;
319 hr = IMediaBuffer_GetBufferAndLength(buffer, &data, &len);
320 if (FAILED(hr))
321 return hr;
323 err = mpg123_feed(This->mh, data, len);
324 if (err != MPG123_OK)
326 ERR("mpg123_feed() failed: %s\n", mpg123_strerror(This->mh));
327 return E_FAIL;
330 return S_OK;
333 static DWORD get_framesize(DMO_MEDIA_TYPE *type)
335 WAVEFORMATEX *format = (WAVEFORMATEX *)type->pbFormat;
336 return 1152 * format->nBlockAlign;
339 static REFERENCE_TIME get_frametime(DMO_MEDIA_TYPE *type)
341 WAVEFORMATEX *format = (WAVEFORMATEX *)type->pbFormat;
342 return (REFERENCE_TIME) 10000000 * 1152 / format->nSamplesPerSec;
345 static HRESULT WINAPI MediaObject_ProcessOutput(IMediaObject *iface, DWORD flags, DWORD count, DMO_OUTPUT_DATA_BUFFER *buffers, DWORD *status)
347 struct mp3_decoder *This = impl_from_IMediaObject(iface);
348 REFERENCE_TIME time = 0, frametime;
349 DWORD len, maxlen, framesize;
350 int got_data = 0;
351 size_t written;
352 HRESULT hr;
353 BYTE *data;
354 int err;
356 TRACE("(%p)->(%#x, %d, %p, %p)\n", iface, flags, count, buffers, status);
358 if (count > 1)
359 FIXME("Multiple buffers not handled.\n");
361 buffers[0].dwStatus = 0;
363 if (!This->buffer)
364 return S_FALSE;
366 buffers[0].dwStatus |= DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT;
368 hr = IMediaBuffer_GetBufferAndLength(buffers[0].pBuffer, &data, &len);
369 if (FAILED(hr)) return hr;
371 hr = IMediaBuffer_GetMaxLength(buffers[0].pBuffer, &maxlen);
372 if (FAILED(hr)) return hr;
374 framesize = get_framesize(&This->outtype);
375 frametime = get_frametime(&This->outtype);
377 while (1)
379 if (maxlen - len < framesize)
381 buffers[0].dwStatus |= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE;
382 break;
385 while ((err = mpg123_read(This->mh, data + len, framesize, &written)) == MPG123_NEW_FORMAT);
386 if (err == MPG123_NEED_MORE)
388 IMediaBuffer_Release(This->buffer);
389 This->buffer = NULL;
390 break;
392 else if (err == MPG123_ERR)
393 ERR("mpg123_read() failed: %s\n", mpg123_strerror(This->mh));
394 else if (err != MPG123_OK)
395 ERR("mpg123_read() returned %d\n", err);
396 if (written < framesize)
397 ERR("short write: %zd/%u\n", written, framesize);
399 got_data = 1;
401 len += framesize;
402 hr = IMediaBuffer_SetLength(buffers[0].pBuffer, len);
403 if (FAILED(hr)) return hr;
405 time += frametime;
408 if (got_data)
410 buffers[0].dwStatus |= (DMO_OUTPUT_DATA_BUFFERF_TIME | DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH);
411 buffers[0].rtTimelength = time;
412 buffers[0].rtTimestamp = This->timestamp;
413 This->timestamp += time;
414 return S_OK;
416 return S_FALSE;
419 static HRESULT WINAPI MediaObject_Lock(IMediaObject *iface, LONG lock)
421 FIXME("(%p)->(%d) stub!\n", iface, lock);
423 return E_NOTIMPL;
426 static const IMediaObjectVtbl MediaObject_vtbl = {
427 MediaObject_QueryInterface,
428 MediaObject_AddRef,
429 MediaObject_Release,
430 MediaObject_GetStreamCount,
431 MediaObject_GetInputStreamInfo,
432 MediaObject_GetOutputStreamInfo,
433 MediaObject_GetInputType,
434 MediaObject_GetOutputType,
435 MediaObject_SetInputType,
436 MediaObject_SetOutputType,
437 MediaObject_GetInputCurrentType,
438 MediaObject_GetOutputCurrentType,
439 MediaObject_GetInputSizeInfo,
440 MediaObject_GetOutputSizeInfo,
441 MediaObject_GetInputMaxLatency,
442 MediaObject_SetInputMaxLatency,
443 MediaObject_Flush,
444 MediaObject_Discontinuity,
445 MediaObject_AllocateStreamingResources,
446 MediaObject_FreeStreamingResources,
447 MediaObject_GetInputStatus,
448 MediaObject_ProcessInput,
449 MediaObject_ProcessOutput,
450 MediaObject_Lock,
453 static HRESULT create_mp3_decoder(IUnknown *outer, REFIID iid, void **obj)
455 struct mp3_decoder *This;
456 HRESULT hr;
457 int err;
459 if (!(This = heap_alloc_zero(sizeof(*This))))
460 return E_OUTOFMEMORY;
462 This->IUnknown_inner.lpVtbl = &Unknown_vtbl;
463 This->IMediaObject_iface.lpVtbl = &MediaObject_vtbl;
464 This->ref = 1;
465 This->outer = outer ? outer : &This->IUnknown_inner;
467 mpg123_init();
468 This->mh = mpg123_new(NULL, &err);
469 mpg123_open_feed(This->mh);
470 mpg123_format_none(This->mh);
472 hr = IUnknown_QueryInterface(&This->IUnknown_inner, iid, obj);
473 IUnknown_Release(&This->IUnknown_inner);
474 return hr;
477 static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID iid, void **obj)
479 TRACE("(%p, %s, %p)\n", iface, debugstr_guid(iid), obj);
481 if (IsEqualGUID(&IID_IUnknown, iid) ||
482 IsEqualGUID(&IID_IClassFactory, iid))
484 IClassFactory_AddRef(iface);
485 *obj = iface;
486 return S_OK;
489 *obj = NULL;
490 WARN("no interface for %s\n", debugstr_guid(iid));
491 return E_NOINTERFACE;
494 static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface)
496 return 2;
499 static ULONG WINAPI ClassFactory_Release(IClassFactory *iface)
501 return 1;
504 static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID iid, void **obj)
506 TRACE("(%p, %s, %p)\n", outer, debugstr_guid(iid), obj);
508 if (outer && !IsEqualGUID(iid, &IID_IUnknown))
510 *obj = NULL;
511 return E_NOINTERFACE;
514 return create_mp3_decoder(outer, iid, obj);
517 static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL lock)
519 FIXME("(%d) stub\n", lock);
520 return S_OK;
523 static const IClassFactoryVtbl classfactory_vtbl = {
524 ClassFactory_QueryInterface,
525 ClassFactory_AddRef,
526 ClassFactory_Release,
527 ClassFactory_CreateInstance,
528 ClassFactory_LockServer
531 static IClassFactory mp3_decoder_cf = { &classfactory_vtbl };
533 BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
535 TRACE("%p, %d, %p\n", instance, reason, reserved);
536 switch (reason)
538 case DLL_PROCESS_ATTACH:
539 DisableThreadLibraryCalls(instance);
540 mp3dmod_instance = instance;
541 break;
543 return TRUE;
546 /*************************************************************************
547 * DllGetClassObject (DSDMO.@)
549 HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **obj)
551 TRACE("%s, %s, %p\n", debugstr_guid(clsid), debugstr_guid(iid), obj);
553 if (IsEqualGUID(clsid, &CLSID_CMP3DecMediaObject))
554 return IClassFactory_QueryInterface(&mp3_decoder_cf, iid, obj);
556 FIXME("class %s not available\n", debugstr_guid(clsid));
557 return CLASS_E_CLASSNOTAVAILABLE;
560 /******************************************************************
561 * DllCanUnloadNow (DSDMO.@)
563 HRESULT WINAPI DllCanUnloadNow(void)
565 return S_FALSE;
568 /***********************************************************************
569 * DllRegisterServer (DSDMO.@)
571 HRESULT WINAPI DllRegisterServer(void)
573 static const WCHAR nameW[] = {'M','P','3',' ','D','e','c','o','d','e','r',' ','D','M','O',0};
574 DMO_PARTIAL_MEDIATYPE in, out;
575 HRESULT hr;
577 in.type = WMMEDIATYPE_Audio;
578 in.subtype = WMMEDIASUBTYPE_MP3;
579 out.type = WMMEDIATYPE_Audio;
580 out.subtype = WMMEDIASUBTYPE_PCM;
581 hr = DMORegister(nameW, &CLSID_CMP3DecMediaObject, &DMOCATEGORY_AUDIO_DECODER,
582 0, 1, &in, 1, &out);
583 if (FAILED(hr)) return hr;
585 return __wine_register_resources( mp3dmod_instance );
588 /***********************************************************************
589 * DllUnregisterServer (DSDMO.@)
591 HRESULT WINAPI DllUnregisterServer(void)
593 HRESULT hr;
595 hr = DMOUnregister(&CLSID_CMP3DecMediaObject, &DMOCATEGORY_AUDIO_DECODER);
596 if (FAILED(hr)) return hr;
598 return __wine_unregister_resources( mp3dmod_instance );