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
32 #include "wmcodecdsp.h"
33 #include "wine/debug.h"
34 #include "wine/heap.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
;
48 IUnknown IUnknown_inner
;
49 IMediaObject IMediaObject_iface
;
54 DMO_MEDIA_TYPE intype
, outtype
;
55 BOOL intype_set
, outtype_set
;
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
;
78 FIXME("no interface for %s\n", debugstr_guid(iid
));
83 IUnknown_AddRef((IUnknown
*)*obj
);
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);
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);
107 IMediaBuffer_Release(This
->buffer
);
108 if (This
->intype_set
)
109 MoFreeMediaType(&This
->intype
);
110 MoFreeMediaType(&This
->outtype
);
111 mpg123_delete(This
->mh
);
117 static const IUnknownVtbl Unknown_vtbl
= {
118 Unknown_QueryInterface
,
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;
155 static HRESULT WINAPI
MediaObject_GetInputStreamInfo(IMediaObject
*iface
, DWORD index
, DWORD
*flags
)
157 TRACE("iface %p, index %u, flags %p.\n", iface
, index
, flags
);
164 static HRESULT WINAPI
MediaObject_GetOutputStreamInfo(IMediaObject
*iface
, DWORD index
, DWORD
*flags
)
166 TRACE("iface %p, index %u, flags %p.\n", iface
, index
, flags
);
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
);
178 return DMO_E_NO_MORE_ITEMS
;
180 type
->majortype
= WMMEDIATYPE_Audio
;
181 type
->subtype
= WMMEDIASUBTYPE_MP3
;
182 type
->formattype
= GUID_NULL
;
185 type
->pbFormat
= NULL
;
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
;
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
;
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
)
234 MoFreeMediaType(&dmo
->intype
);
235 dmo
->intype_set
= FALSE
;
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
))
247 MoFreeMediaType(&dmo
->intype
);
248 MoCopyMediaType(&dmo
->intype
, type
);
249 dmo
->intype_set
= TRUE
;
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
;
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
;
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
;
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
;
302 static HRESULT WINAPI
MediaObject_GetInputCurrentType(IMediaObject
*iface
, DWORD index
, DMO_MEDIA_TYPE
*type
)
304 FIXME("(%p)->(%d, %p) stub!\n", iface
, index
, type
);
309 static HRESULT WINAPI
MediaObject_GetOutputCurrentType(IMediaObject
*iface
, DWORD index
, DMO_MEDIA_TYPE
*type
)
311 FIXME("(%p)->(%d, %p) stub!\n", iface
, index
, type
);
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
;
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;
345 static HRESULT WINAPI
MediaObject_GetInputMaxLatency(IMediaObject
*iface
, DWORD index
, REFERENCE_TIME
*latency
)
347 FIXME("(%p)->(%d, %p) stub!\n", iface
, index
, latency
);
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
));
359 static HRESULT WINAPI
MediaObject_Flush(IMediaObject
*iface
)
361 struct mp3_decoder
*dmo
= impl_from_IMediaObject(iface
);
363 TRACE("iface %p.\n", iface
);
366 IMediaBuffer_Release(dmo
->buffer
);
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
);
377 static HRESULT WINAPI
MediaObject_Discontinuity(IMediaObject
*iface
, DWORD index
)
379 TRACE("iface %p.\n", iface
);
384 static HRESULT WINAPI
MediaObject_AllocateStreamingResources(IMediaObject
*iface
)
386 FIXME("(%p)->() stub!\n", iface
);
391 static HRESULT WINAPI
MediaObject_FreeStreamingResources(IMediaObject
*iface
)
393 FIXME("(%p)->() stub!\n", iface
);
398 static HRESULT WINAPI
MediaObject_GetInputStatus(IMediaObject
*iface
, DWORD index
, DWORD
*flags
)
400 FIXME("(%p)->(%d, %p) stub!\n", iface
, index
, flags
);
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
);
414 TRACE("(%p)->(%d, %p, %#x, %s, %s)\n", iface
, index
, buffer
, flags
,
415 wine_dbgstr_longlong(timestamp
), wine_dbgstr_longlong(timelength
));
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
);
430 err
= mpg123_feed(This
->mh
, data
, len
);
431 if (err
!= MPG123_OK
)
433 ERR("mpg123_feed() failed: %s\n", mpg123_strerror(This
->mh
));
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
;
463 TRACE("(%p)->(%#x, %d, %p, %p)\n", iface
, flags
, count
, buffers
, status
);
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
)
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
;
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
);
500 if (maxlen
- len
< framesize
)
502 buffers
[0].dwStatus
|= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE
;
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
);
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
);
523 hr
= IMediaBuffer_SetLength(buffers
[0].pBuffer
, len
);
524 if (FAILED(hr
)) return hr
;
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
;
540 static HRESULT WINAPI
MediaObject_Lock(IMediaObject
*iface
, LONG lock
)
542 FIXME("(%p)->(%d) stub!\n", iface
, lock
);
547 static const IMediaObjectVtbl MediaObject_vtbl
= {
548 MediaObject_QueryInterface
,
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
,
565 MediaObject_Discontinuity
,
566 MediaObject_AllocateStreamingResources
,
567 MediaObject_FreeStreamingResources
,
568 MediaObject_GetInputStatus
,
569 MediaObject_ProcessInput
,
570 MediaObject_ProcessOutput
,
574 static HRESULT
create_mp3_decoder(IUnknown
*outer
, REFIID iid
, void **obj
)
576 struct mp3_decoder
*This
;
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
;
586 This
->outer
= outer
? outer
: &This
->IUnknown_inner
;
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
);
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
);
611 WARN("no interface for %s\n", debugstr_guid(iid
));
612 return E_NOINTERFACE
;
615 static ULONG WINAPI
ClassFactory_AddRef(IClassFactory
*iface
)
620 static ULONG WINAPI
ClassFactory_Release(IClassFactory
*iface
)
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
))
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
);
644 static const IClassFactoryVtbl classfactory_vtbl
= {
645 ClassFactory_QueryInterface
,
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
);
659 case DLL_PROCESS_ATTACH
:
660 DisableThreadLibraryCalls(instance
);
661 mp3dmod_instance
= instance
;
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)
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
;
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
,
704 if (FAILED(hr
)) return hr
;
706 return __wine_register_resources( mp3dmod_instance
);
709 /***********************************************************************
710 * DllUnregisterServer (DSDMO.@)
712 HRESULT WINAPI
DllUnregisterServer(void)
716 hr
= DMOUnregister(&CLSID_CMP3DecMediaObject
, &DMOCATEGORY_AUDIO_DECODER
);
717 if (FAILED(hr
)) return hr
;
719 return __wine_unregister_resources( mp3dmod_instance
);