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);
41 WINE_DEFAULT_DEBUG_CHANNEL(mp3dmod
);
43 static HINSTANCE mp3dmod_instance
;
46 IUnknown IUnknown_inner
;
47 IMediaObject IMediaObject_iface
;
51 DMO_MEDIA_TYPE outtype
;
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
;
73 FIXME("no interface for %s\n", debugstr_guid(iid
));
78 IUnknown_AddRef((IUnknown
*)*obj
);
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);
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);
101 MoFreeMediaType(&This
->outtype
);
102 mpg123_delete(This
->mh
);
108 static const IUnknownVtbl Unknown_vtbl
= {
109 Unknown_QueryInterface
,
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
);
144 static HRESULT WINAPI
MediaObject_GetInputStreamInfo(IMediaObject
*iface
, DWORD index
, DWORD
*flags
)
146 FIXME("(%p)->(%d, %p) stub!\n", iface
, index
, flags
);
151 static HRESULT WINAPI
MediaObject_GetOutputStreamInfo(IMediaObject
*iface
, DWORD index
, DWORD
*flags
)
153 FIXME("(%p)->(%d, %p) stub!\n", iface
, index
, flags
);
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
);
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
);
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
);
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
;
186 TRACE("(%p)->(%d, %p, %#x)\n", iface
, index
, type
, flags
);
188 if (flags
& DMO_SET_TYPEF_CLEAR
)
190 MoFreeMediaType(&This
->outtype
);
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
;
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
);
221 static HRESULT WINAPI
MediaObject_GetInputCurrentType(IMediaObject
*iface
, DWORD index
, DMO_MEDIA_TYPE
*type
)
223 FIXME("(%p)->(%d, %p) stub!\n", iface
, index
, type
);
228 static HRESULT WINAPI
MediaObject_GetOutputCurrentType(IMediaObject
*iface
, DWORD index
, DMO_MEDIA_TYPE
*type
)
230 FIXME("(%p)->(%d, %p) stub!\n", iface
, index
, type
);
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
);
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
);
249 static HRESULT WINAPI
MediaObject_GetInputMaxLatency(IMediaObject
*iface
, DWORD index
, REFERENCE_TIME
*latency
)
251 FIXME("(%p)->(%d, %p) stub!\n", iface
, index
, latency
);
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
));
263 static HRESULT WINAPI
MediaObject_Flush(IMediaObject
*iface
)
265 FIXME("(%p)->() stub!\n", iface
);
270 static HRESULT WINAPI
MediaObject_Discontinuity(IMediaObject
*iface
, DWORD index
)
272 FIXME("(%p)->(%d) stub!\n", iface
, index
);
277 static HRESULT WINAPI
MediaObject_AllocateStreamingResources(IMediaObject
*iface
)
279 FIXME("(%p)->() stub!\n", iface
);
284 static HRESULT WINAPI
MediaObject_FreeStreamingResources(IMediaObject
*iface
)
286 FIXME("(%p)->() stub!\n", iface
);
291 static HRESULT WINAPI
MediaObject_GetInputStatus(IMediaObject
*iface
, DWORD index
, DWORD
*flags
)
293 FIXME("(%p)->(%d, %p) stub!\n", iface
, index
, flags
);
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
);
307 TRACE("(%p)->(%d, %p, %#x, %s, %s)\n", iface
, index
, buffer
, flags
,
308 wine_dbgstr_longlong(timestamp
), wine_dbgstr_longlong(timelength
));
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
);
323 err
= mpg123_feed(This
->mh
, data
, len
);
324 if (err
!= MPG123_OK
)
326 ERR("mpg123_feed() failed: %s\n", mpg123_strerror(This
->mh
));
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
;
356 TRACE("(%p)->(%#x, %d, %p, %p)\n", iface
, flags
, count
, buffers
, status
);
359 FIXME("Multiple buffers not handled.\n");
361 buffers
[0].dwStatus
= 0;
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
);
379 if (maxlen
- len
< framesize
)
381 buffers
[0].dwStatus
|= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE
;
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
);
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
);
402 hr
= IMediaBuffer_SetLength(buffers
[0].pBuffer
, len
);
403 if (FAILED(hr
)) return hr
;
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
;
419 static HRESULT WINAPI
MediaObject_Lock(IMediaObject
*iface
, LONG lock
)
421 FIXME("(%p)->(%d) stub!\n", iface
, lock
);
426 static const IMediaObjectVtbl MediaObject_vtbl
= {
427 MediaObject_QueryInterface
,
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
,
444 MediaObject_Discontinuity
,
445 MediaObject_AllocateStreamingResources
,
446 MediaObject_FreeStreamingResources
,
447 MediaObject_GetInputStatus
,
448 MediaObject_ProcessInput
,
449 MediaObject_ProcessOutput
,
453 static HRESULT
create_mp3_decoder(IUnknown
*outer
, REFIID iid
, void **obj
)
455 struct mp3_decoder
*This
;
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
;
465 This
->outer
= outer
? outer
: &This
->IUnknown_inner
;
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
);
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
);
490 WARN("no interface for %s\n", debugstr_guid(iid
));
491 return E_NOINTERFACE
;
494 static ULONG WINAPI
ClassFactory_AddRef(IClassFactory
*iface
)
499 static ULONG WINAPI
ClassFactory_Release(IClassFactory
*iface
)
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
))
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
);
523 static const IClassFactoryVtbl classfactory_vtbl
= {
524 ClassFactory_QueryInterface
,
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
);
538 case DLL_PROCESS_ATTACH
:
539 DisableThreadLibraryCalls(instance
);
540 mp3dmod_instance
= instance
;
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)
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
;
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
,
583 if (FAILED(hr
)) return hr
;
585 return __wine_register_resources( mp3dmod_instance
);
588 /***********************************************************************
589 * DllUnregisterServer (DSDMO.@)
591 HRESULT WINAPI
DllUnregisterServer(void)
595 hr
= DMOUnregister(&CLSID_CMP3DecMediaObject
, &DMOCATEGORY_AUDIO_DECODER
);
596 if (FAILED(hr
)) return hr
;
598 return __wine_unregister_resources( mp3dmod_instance
);