winegstreamer: Move the "flip" field to struct wg_parser_stream.
[wine/zf.git] / dlls / mp3dmod / mp3dmod.c
blob3ad3b785d2930ee6cda6d8acc53ffafe73f13179
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);
40 DEFINE_GUID(WMFORMAT_WaveFormatEx, 0x05589f81,0xc356,0x11ce,0xbf,0x01,0x00,0xaa,0x00,0x55,0x59,0x5a);
42 WINE_DEFAULT_DEBUG_CHANNEL(mp3dmod);
44 static HINSTANCE mp3dmod_instance;
46 struct mp3_decoder
48 IUnknown IUnknown_inner;
49 IMediaObject IMediaObject_iface;
50 IUnknown *outer;
51 LONG ref;
52 mpg123_handle *mh;
54 DMO_MEDIA_TYPE intype, outtype;
55 BOOL intype_set, outtype_set;
57 IMediaBuffer *buffer;
58 REFERENCE_TIME timestamp;
61 static inline struct mp3_decoder *impl_from_IUnknown(IUnknown *iface)
63 return CONTAINING_RECORD(iface, struct mp3_decoder, IUnknown_inner);
66 static HRESULT WINAPI Unknown_QueryInterface(IUnknown *iface, REFIID iid, void **obj)
68 struct mp3_decoder *This = impl_from_IUnknown(iface);
70 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(iid), obj);
72 if (IsEqualGUID(iid, &IID_IUnknown))
73 *obj = &This->IUnknown_inner;
74 else if (IsEqualGUID(iid, &IID_IMediaObject))
75 *obj = &This->IMediaObject_iface;
76 else
78 FIXME("no interface for %s\n", debugstr_guid(iid));
79 *obj = NULL;
80 return E_NOINTERFACE;
83 IUnknown_AddRef((IUnknown *)*obj);
84 return S_OK;
87 static ULONG WINAPI Unknown_AddRef(IUnknown *iface)
89 struct mp3_decoder *This = impl_from_IUnknown(iface);
90 ULONG refcount = InterlockedIncrement(&This->ref);
92 TRACE("(%p) AddRef from %d\n", This, refcount - 1);
94 return refcount;
97 static ULONG WINAPI Unknown_Release(IUnknown *iface)
99 struct mp3_decoder *This = impl_from_IUnknown(iface);
100 ULONG refcount = InterlockedDecrement(&This->ref);
102 TRACE("(%p) Release from %d\n", This, refcount + 1);
104 if (!refcount)
106 if (This->buffer)
107 IMediaBuffer_Release(This->buffer);
108 if (This->intype_set)
109 MoFreeMediaType(&This->intype);
110 MoFreeMediaType(&This->outtype);
111 mpg123_delete(This->mh);
112 heap_free(This);
114 return refcount;
117 static const IUnknownVtbl Unknown_vtbl = {
118 Unknown_QueryInterface,
119 Unknown_AddRef,
120 Unknown_Release,
123 static inline struct mp3_decoder *impl_from_IMediaObject(IMediaObject *iface)
125 return CONTAINING_RECORD(iface, struct mp3_decoder, IMediaObject_iface);
128 static HRESULT WINAPI MediaObject_QueryInterface(IMediaObject *iface, REFIID iid, void **obj)
130 struct mp3_decoder *This = impl_from_IMediaObject(iface);
131 return IUnknown_QueryInterface(This->outer, iid, obj);
134 static ULONG WINAPI MediaObject_AddRef(IMediaObject *iface)
136 struct mp3_decoder *This = impl_from_IMediaObject(iface);
137 return IUnknown_AddRef(This->outer);
140 static ULONG WINAPI MediaObject_Release(IMediaObject *iface)
142 struct mp3_decoder *This = impl_from_IMediaObject(iface);
143 return IUnknown_Release(This->outer);
146 static HRESULT WINAPI MediaObject_GetStreamCount(IMediaObject *iface, DWORD *input, DWORD *output)
148 TRACE("iface %p, input %p, output %p.\n", iface, input, output);
150 *input = *output = 1;
152 return S_OK;
155 static HRESULT WINAPI MediaObject_GetInputStreamInfo(IMediaObject *iface, DWORD index, DWORD *flags)
157 TRACE("iface %p, index %u, flags %p.\n", iface, index, flags);
159 *flags = 0;
161 return S_OK;
164 static HRESULT WINAPI MediaObject_GetOutputStreamInfo(IMediaObject *iface, DWORD index, DWORD *flags)
166 TRACE("iface %p, index %u, flags %p.\n", iface, index, flags);
168 *flags = 0;
170 return S_OK;
173 static HRESULT WINAPI MediaObject_GetInputType(IMediaObject *iface, DWORD index, DWORD type_index, DMO_MEDIA_TYPE *type)
175 TRACE("iface %p, index %u, type_index %u, type %p.\n", iface, index, type_index, type);
177 if (type_index)
178 return DMO_E_NO_MORE_ITEMS;
180 type->majortype = WMMEDIATYPE_Audio;
181 type->subtype = WMMEDIASUBTYPE_MP3;
182 type->formattype = GUID_NULL;
183 type->pUnk = NULL;
184 type->cbFormat = 0;
185 type->pbFormat = NULL;
187 return S_OK;
190 static HRESULT WINAPI MediaObject_GetOutputType(IMediaObject *iface, DWORD index, DWORD type_index, DMO_MEDIA_TYPE *type)
192 struct mp3_decoder *dmo = impl_from_IMediaObject(iface);
193 const WAVEFORMATEX *input_format;
194 WAVEFORMATEX *format;
196 TRACE("iface %p, index %u, type_index %u, type %p.\n", iface, index, type_index, type);
198 if (!dmo->intype_set)
199 return DMO_E_TYPE_NOT_SET;
201 input_format = (WAVEFORMATEX *)dmo->intype.pbFormat;
203 if (type_index >= (2 * input_format->nChannels))
204 return DMO_E_NO_MORE_ITEMS;
206 type->majortype = WMMEDIATYPE_Audio;
207 type->subtype = WMMEDIASUBTYPE_PCM;
208 type->formattype = WMFORMAT_WaveFormatEx;
209 type->pUnk = NULL;
210 type->cbFormat = sizeof(WAVEFORMATEX);
211 if (!(type->pbFormat = CoTaskMemAlloc(sizeof(WAVEFORMATEX))))
212 return E_OUTOFMEMORY;
213 format = (WAVEFORMATEX *)type->pbFormat;
214 format->wFormatTag = WAVE_FORMAT_PCM;
215 format->nSamplesPerSec = input_format->nSamplesPerSec;
216 format->nChannels = (type_index / 2) ? 1 : input_format->nChannels;
217 format->wBitsPerSample = (type_index % 2) ? 8 : 16;
218 format->nBlockAlign = format->nChannels * format->wBitsPerSample / 8;
219 format->nAvgBytesPerSec = format->nSamplesPerSec * format->nBlockAlign;
220 format->cbSize = 0;
222 return S_OK;
225 static HRESULT WINAPI MediaObject_SetInputType(IMediaObject *iface, DWORD index, const DMO_MEDIA_TYPE *type, DWORD flags)
227 struct mp3_decoder *dmo = impl_from_IMediaObject(iface);
229 TRACE("iface %p, index %u, type %p, flags %#x.\n", iface, index, type, flags);
231 if (flags & DMO_SET_TYPEF_CLEAR)
233 if (dmo->intype_set)
234 MoFreeMediaType(&dmo->intype);
235 dmo->intype_set = FALSE;
236 return S_OK;
239 if (!IsEqualGUID(&type->majortype, &WMMEDIATYPE_Audio)
240 || !IsEqualGUID(&type->subtype, &WMMEDIASUBTYPE_MP3)
241 || !IsEqualGUID(&type->formattype, &WMFORMAT_WaveFormatEx))
242 return DMO_E_TYPE_NOT_ACCEPTED;
244 if (!(flags & DMO_SET_TYPEF_TEST_ONLY))
246 if (dmo->intype_set)
247 MoFreeMediaType(&dmo->intype);
248 MoCopyMediaType(&dmo->intype, type);
249 dmo->intype_set = TRUE;
252 return S_OK;
255 static HRESULT WINAPI MediaObject_SetOutputType(IMediaObject *iface, DWORD index, const DMO_MEDIA_TYPE *type, DWORD flags)
257 struct mp3_decoder *This = impl_from_IMediaObject(iface);
258 WAVEFORMATEX *format;
259 long enc;
260 int err;
262 TRACE("(%p)->(%d, %p, %#x)\n", iface, index, type, flags);
264 if (flags & DMO_SET_TYPEF_CLEAR)
266 MoFreeMediaType(&This->outtype);
267 This->outtype_set = FALSE;
268 return S_OK;
271 if (!IsEqualGUID(&type->formattype, &WMFORMAT_WaveFormatEx))
272 return DMO_E_TYPE_NOT_ACCEPTED;
274 format = (WAVEFORMATEX *)type->pbFormat;
276 if (format->wBitsPerSample == 8)
277 enc = MPG123_ENC_UNSIGNED_8;
278 else if (format->wBitsPerSample == 16)
279 enc = MPG123_ENC_SIGNED_16;
280 else
282 ERR("Cannot decode to bit depth %u.\n", format->wBitsPerSample);
283 return DMO_E_TYPE_NOT_ACCEPTED;
286 if (!(flags & DMO_SET_TYPEF_TEST_ONLY))
288 err = mpg123_format(This->mh, format->nSamplesPerSec, format->nChannels, enc);
289 if (err != MPG123_OK)
291 ERR("Failed to set format: %u channels, %u samples/sec, %u bits/sample.\n",
292 format->nChannels, format->nSamplesPerSec, format->wBitsPerSample);
293 return DMO_E_TYPE_NOT_ACCEPTED;
295 MoCopyMediaType(&This->outtype, type);
296 This->outtype_set = TRUE;
299 return S_OK;
302 static HRESULT WINAPI MediaObject_GetInputCurrentType(IMediaObject *iface, DWORD index, DMO_MEDIA_TYPE *type)
304 FIXME("(%p)->(%d, %p) stub!\n", iface, index, type);
306 return E_NOTIMPL;
309 static HRESULT WINAPI MediaObject_GetOutputCurrentType(IMediaObject *iface, DWORD index, DMO_MEDIA_TYPE *type)
311 FIXME("(%p)->(%d, %p) stub!\n", iface, index, type);
313 return E_NOTIMPL;
316 static HRESULT WINAPI MediaObject_GetInputSizeInfo(IMediaObject *iface,
317 DWORD index, DWORD *size, DWORD *lookahead, DWORD *alignment)
319 struct mp3_decoder *dmo = impl_from_IMediaObject(iface);
321 TRACE("iface %p, index %u, size %p, lookahead %p, alignment %p.\n", iface, index, size, lookahead, alignment);
323 if (!dmo->intype_set || !dmo->outtype_set)
324 return DMO_E_TYPE_NOT_SET;
326 *size = 0;
327 *alignment = 1;
328 return S_OK;
331 static HRESULT WINAPI MediaObject_GetOutputSizeInfo(IMediaObject *iface, DWORD index, DWORD *size, DWORD *alignment)
333 struct mp3_decoder *dmo = impl_from_IMediaObject(iface);
335 TRACE("iface %p, index %u, size %p, alignment %p.\n", iface, index, size, alignment);
337 if (!dmo->intype_set || !dmo->outtype_set)
338 return DMO_E_TYPE_NOT_SET;
340 *size = 2 * 1152 * ((WAVEFORMATEX *)dmo->outtype.pbFormat)->wBitsPerSample / 8;
341 *alignment = 1;
342 return S_OK;
345 static HRESULT WINAPI MediaObject_GetInputMaxLatency(IMediaObject *iface, DWORD index, REFERENCE_TIME *latency)
347 FIXME("(%p)->(%d, %p) stub!\n", iface, index, latency);
349 return E_NOTIMPL;
352 static HRESULT WINAPI MediaObject_SetInputMaxLatency(IMediaObject *iface, DWORD index, REFERENCE_TIME latency)
354 FIXME("(%p)->(%d, %s) stub!\n", iface, index, wine_dbgstr_longlong(latency));
356 return E_NOTIMPL;
359 static HRESULT WINAPI MediaObject_Flush(IMediaObject *iface)
361 struct mp3_decoder *dmo = impl_from_IMediaObject(iface);
363 TRACE("iface %p.\n", iface);
365 if (dmo->buffer)
366 IMediaBuffer_Release(dmo->buffer);
367 dmo->buffer = NULL;
368 dmo->timestamp = 0;
370 /* mpg123 doesn't give us a way to flush, so just close and reopen the feed. */
371 mpg123_close(dmo->mh);
372 mpg123_open_feed(dmo->mh);
374 return S_OK;
377 static HRESULT WINAPI MediaObject_Discontinuity(IMediaObject *iface, DWORD index)
379 TRACE("iface %p.\n", iface);
381 return S_OK;
384 static HRESULT WINAPI MediaObject_AllocateStreamingResources(IMediaObject *iface)
386 FIXME("(%p)->() stub!\n", iface);
388 return E_NOTIMPL;
391 static HRESULT WINAPI MediaObject_FreeStreamingResources(IMediaObject *iface)
393 FIXME("(%p)->() stub!\n", iface);
395 return E_NOTIMPL;
398 static HRESULT WINAPI MediaObject_GetInputStatus(IMediaObject *iface, DWORD index, DWORD *flags)
400 FIXME("(%p)->(%d, %p) stub!\n", iface, index, flags);
402 return E_NOTIMPL;
405 static HRESULT WINAPI MediaObject_ProcessInput(IMediaObject *iface, DWORD index,
406 IMediaBuffer *buffer, DWORD flags, REFERENCE_TIME timestamp, REFERENCE_TIME timelength)
408 struct mp3_decoder *This = impl_from_IMediaObject(iface);
409 HRESULT hr;
410 BYTE *data;
411 DWORD len;
412 int err;
414 TRACE("(%p)->(%d, %p, %#x, %s, %s)\n", iface, index, buffer, flags,
415 wine_dbgstr_longlong(timestamp), wine_dbgstr_longlong(timelength));
417 if (This->buffer)
419 ERR("Already have a buffer.\n");
420 return DMO_E_NOTACCEPTING;
423 IMediaBuffer_AddRef(buffer);
424 This->buffer = buffer;
426 hr = IMediaBuffer_GetBufferAndLength(buffer, &data, &len);
427 if (FAILED(hr))
428 return hr;
430 err = mpg123_feed(This->mh, data, len);
431 if (err != MPG123_OK)
433 ERR("mpg123_feed() failed: %s\n", mpg123_strerror(This->mh));
434 return E_FAIL;
437 return S_OK;
440 static DWORD get_framesize(DMO_MEDIA_TYPE *type)
442 WAVEFORMATEX *format = (WAVEFORMATEX *)type->pbFormat;
443 return 1152 * format->nBlockAlign;
446 static REFERENCE_TIME get_frametime(DMO_MEDIA_TYPE *type)
448 WAVEFORMATEX *format = (WAVEFORMATEX *)type->pbFormat;
449 return (REFERENCE_TIME) 10000000 * 1152 / format->nSamplesPerSec;
452 static HRESULT WINAPI MediaObject_ProcessOutput(IMediaObject *iface, DWORD flags, DWORD count, DMO_OUTPUT_DATA_BUFFER *buffers, DWORD *status)
454 struct mp3_decoder *This = impl_from_IMediaObject(iface);
455 REFERENCE_TIME time = 0, frametime;
456 DWORD len, maxlen, framesize;
457 int got_data = 0;
458 size_t written;
459 HRESULT hr;
460 BYTE *data;
461 int err;
463 TRACE("(%p)->(%#x, %d, %p, %p)\n", iface, flags, count, buffers, status);
465 if (count > 1)
466 FIXME("Multiple buffers not handled.\n");
468 buffers[0].dwStatus = 0;
470 if (!buffers[0].pBuffer)
472 while ((err = mpg123_read(This->mh, NULL, 0, &written)) == MPG123_NEW_FORMAT);
473 if (err == MPG123_NEED_MORE)
474 return S_OK;
475 else if (err == MPG123_ERR)
476 ERR("mpg123_read() failed: %s\n", mpg123_strerror(This->mh));
477 else if (err != MPG123_OK)
478 ERR("mpg123_read() returned %d\n", err);
480 buffers[0].dwStatus = DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE;
481 return S_OK;
484 if (!This->buffer)
485 return S_FALSE;
487 buffers[0].dwStatus |= DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT;
489 hr = IMediaBuffer_GetBufferAndLength(buffers[0].pBuffer, &data, &len);
490 if (FAILED(hr)) return hr;
492 hr = IMediaBuffer_GetMaxLength(buffers[0].pBuffer, &maxlen);
493 if (FAILED(hr)) return hr;
495 framesize = get_framesize(&This->outtype);
496 frametime = get_frametime(&This->outtype);
498 while (1)
500 if (maxlen - len < framesize)
502 buffers[0].dwStatus |= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE;
503 break;
506 while ((err = mpg123_read(This->mh, data + len, framesize, &written)) == MPG123_NEW_FORMAT);
507 if (err == MPG123_NEED_MORE)
509 IMediaBuffer_Release(This->buffer);
510 This->buffer = NULL;
511 break;
513 else if (err == MPG123_ERR)
514 ERR("mpg123_read() failed: %s\n", mpg123_strerror(This->mh));
515 else if (err != MPG123_OK)
516 ERR("mpg123_read() returned %d\n", err);
517 if (written < framesize)
518 ERR("short write: %zd/%u\n", written, framesize);
520 got_data = 1;
522 len += framesize;
523 hr = IMediaBuffer_SetLength(buffers[0].pBuffer, len);
524 if (FAILED(hr)) return hr;
526 time += frametime;
529 if (got_data)
531 buffers[0].dwStatus |= (DMO_OUTPUT_DATA_BUFFERF_TIME | DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH);
532 buffers[0].rtTimelength = time;
533 buffers[0].rtTimestamp = This->timestamp;
534 This->timestamp += time;
535 return S_OK;
537 return S_FALSE;
540 static HRESULT WINAPI MediaObject_Lock(IMediaObject *iface, LONG lock)
542 FIXME("(%p)->(%d) stub!\n", iface, lock);
544 return E_NOTIMPL;
547 static const IMediaObjectVtbl MediaObject_vtbl = {
548 MediaObject_QueryInterface,
549 MediaObject_AddRef,
550 MediaObject_Release,
551 MediaObject_GetStreamCount,
552 MediaObject_GetInputStreamInfo,
553 MediaObject_GetOutputStreamInfo,
554 MediaObject_GetInputType,
555 MediaObject_GetOutputType,
556 MediaObject_SetInputType,
557 MediaObject_SetOutputType,
558 MediaObject_GetInputCurrentType,
559 MediaObject_GetOutputCurrentType,
560 MediaObject_GetInputSizeInfo,
561 MediaObject_GetOutputSizeInfo,
562 MediaObject_GetInputMaxLatency,
563 MediaObject_SetInputMaxLatency,
564 MediaObject_Flush,
565 MediaObject_Discontinuity,
566 MediaObject_AllocateStreamingResources,
567 MediaObject_FreeStreamingResources,
568 MediaObject_GetInputStatus,
569 MediaObject_ProcessInput,
570 MediaObject_ProcessOutput,
571 MediaObject_Lock,
574 static HRESULT create_mp3_decoder(IUnknown *outer, REFIID iid, void **obj)
576 struct mp3_decoder *This;
577 HRESULT hr;
578 int err;
580 if (!(This = heap_alloc_zero(sizeof(*This))))
581 return E_OUTOFMEMORY;
583 This->IUnknown_inner.lpVtbl = &Unknown_vtbl;
584 This->IMediaObject_iface.lpVtbl = &MediaObject_vtbl;
585 This->ref = 1;
586 This->outer = outer ? outer : &This->IUnknown_inner;
588 mpg123_init();
589 This->mh = mpg123_new(NULL, &err);
590 mpg123_open_feed(This->mh);
591 mpg123_format_none(This->mh);
593 hr = IUnknown_QueryInterface(&This->IUnknown_inner, iid, obj);
594 IUnknown_Release(&This->IUnknown_inner);
595 return hr;
598 static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID iid, void **obj)
600 TRACE("(%p, %s, %p)\n", iface, debugstr_guid(iid), obj);
602 if (IsEqualGUID(&IID_IUnknown, iid) ||
603 IsEqualGUID(&IID_IClassFactory, iid))
605 IClassFactory_AddRef(iface);
606 *obj = iface;
607 return S_OK;
610 *obj = NULL;
611 WARN("no interface for %s\n", debugstr_guid(iid));
612 return E_NOINTERFACE;
615 static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface)
617 return 2;
620 static ULONG WINAPI ClassFactory_Release(IClassFactory *iface)
622 return 1;
625 static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID iid, void **obj)
627 TRACE("(%p, %s, %p)\n", outer, debugstr_guid(iid), obj);
629 if (outer && !IsEqualGUID(iid, &IID_IUnknown))
631 *obj = NULL;
632 return E_NOINTERFACE;
635 return create_mp3_decoder(outer, iid, obj);
638 static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL lock)
640 FIXME("(%d) stub\n", lock);
641 return S_OK;
644 static const IClassFactoryVtbl classfactory_vtbl = {
645 ClassFactory_QueryInterface,
646 ClassFactory_AddRef,
647 ClassFactory_Release,
648 ClassFactory_CreateInstance,
649 ClassFactory_LockServer
652 static IClassFactory mp3_decoder_cf = { &classfactory_vtbl };
654 BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
656 TRACE("%p, %d, %p\n", instance, reason, reserved);
657 switch (reason)
659 case DLL_PROCESS_ATTACH:
660 DisableThreadLibraryCalls(instance);
661 mp3dmod_instance = instance;
662 break;
664 return TRUE;
667 /*************************************************************************
668 * DllGetClassObject (DSDMO.@)
670 HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void **obj)
672 TRACE("%s, %s, %p\n", debugstr_guid(clsid), debugstr_guid(iid), obj);
674 if (IsEqualGUID(clsid, &CLSID_CMP3DecMediaObject))
675 return IClassFactory_QueryInterface(&mp3_decoder_cf, iid, obj);
677 FIXME("class %s not available\n", debugstr_guid(clsid));
678 return CLASS_E_CLASSNOTAVAILABLE;
681 /******************************************************************
682 * DllCanUnloadNow (DSDMO.@)
684 HRESULT WINAPI DllCanUnloadNow(void)
686 return S_FALSE;
689 /***********************************************************************
690 * DllRegisterServer (DSDMO.@)
692 HRESULT WINAPI DllRegisterServer(void)
694 static const WCHAR nameW[] = {'M','P','3',' ','D','e','c','o','d','e','r',' ','D','M','O',0};
695 DMO_PARTIAL_MEDIATYPE in, out;
696 HRESULT hr;
698 in.type = WMMEDIATYPE_Audio;
699 in.subtype = WMMEDIASUBTYPE_MP3;
700 out.type = WMMEDIATYPE_Audio;
701 out.subtype = WMMEDIASUBTYPE_PCM;
702 hr = DMORegister(nameW, &CLSID_CMP3DecMediaObject, &DMOCATEGORY_AUDIO_DECODER,
703 0, 1, &in, 1, &out);
704 if (FAILED(hr)) return hr;
706 return __wine_register_resources( mp3dmod_instance );
709 /***********************************************************************
710 * DllUnregisterServer (DSDMO.@)
712 HRESULT WINAPI DllUnregisterServer(void)
714 HRESULT hr;
716 hr = DMOUnregister(&CLSID_CMP3DecMediaObject, &DMOCATEGORY_AUDIO_DECODER);
717 if (FAILED(hr)) return hr;
719 return __wine_unregister_resources( mp3dmod_instance );