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"
36 DEFINE_GUID(WMMEDIATYPE_Audio
, 0x73647561,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
37 DEFINE_GUID(WMMEDIASUBTYPE_MP3
,0x00000055,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
38 DEFINE_GUID(WMMEDIASUBTYPE_PCM
,0x00000001,0x0000,0x0010,0x80,0x00,0x00,0xaa,0x00,0x38,0x9b,0x71);
39 DEFINE_GUID(WMFORMAT_WaveFormatEx
, 0x05589f81,0xc356,0x11ce,0xbf,0x01,0x00,0xaa,0x00,0x55,0x59,0x5a);
41 WINE_DEFAULT_DEBUG_CHANNEL(mp3dmod
);
45 IUnknown IUnknown_inner
;
46 IMediaObject IMediaObject_iface
;
51 DMO_MEDIA_TYPE intype
, outtype
;
52 BOOL intype_set
, outtype_set
;
55 REFERENCE_TIME timestamp
;
58 static inline struct mp3_decoder
*impl_from_IUnknown(IUnknown
*iface
)
60 return CONTAINING_RECORD(iface
, struct mp3_decoder
, IUnknown_inner
);
63 static HRESULT WINAPI
Unknown_QueryInterface(IUnknown
*iface
, REFIID iid
, void **obj
)
65 struct mp3_decoder
*This
= impl_from_IUnknown(iface
);
67 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(iid
), obj
);
69 if (IsEqualGUID(iid
, &IID_IUnknown
))
70 *obj
= &This
->IUnknown_inner
;
71 else if (IsEqualGUID(iid
, &IID_IMediaObject
))
72 *obj
= &This
->IMediaObject_iface
;
75 FIXME("no interface for %s\n", debugstr_guid(iid
));
80 IUnknown_AddRef((IUnknown
*)*obj
);
84 static ULONG WINAPI
Unknown_AddRef(IUnknown
*iface
)
86 struct mp3_decoder
*This
= impl_from_IUnknown(iface
);
87 ULONG refcount
= InterlockedIncrement(&This
->ref
);
89 TRACE("(%p) AddRef from %ld\n", This
, refcount
- 1);
94 static ULONG WINAPI
Unknown_Release(IUnknown
*iface
)
96 struct mp3_decoder
*This
= impl_from_IUnknown(iface
);
97 ULONG refcount
= InterlockedDecrement(&This
->ref
);
99 TRACE("(%p) Release from %ld\n", This
, refcount
+ 1);
104 IMediaBuffer_Release(This
->buffer
);
105 if (This
->intype_set
)
106 MoFreeMediaType(&This
->intype
);
107 MoFreeMediaType(&This
->outtype
);
108 mpg123_delete(This
->mh
);
114 static const IUnknownVtbl Unknown_vtbl
= {
115 Unknown_QueryInterface
,
120 static inline struct mp3_decoder
*impl_from_IMediaObject(IMediaObject
*iface
)
122 return CONTAINING_RECORD(iface
, struct mp3_decoder
, IMediaObject_iface
);
125 static HRESULT WINAPI
MediaObject_QueryInterface(IMediaObject
*iface
, REFIID iid
, void **obj
)
127 struct mp3_decoder
*This
= impl_from_IMediaObject(iface
);
128 return IUnknown_QueryInterface(This
->outer
, iid
, obj
);
131 static ULONG WINAPI
MediaObject_AddRef(IMediaObject
*iface
)
133 struct mp3_decoder
*This
= impl_from_IMediaObject(iface
);
134 return IUnknown_AddRef(This
->outer
);
137 static ULONG WINAPI
MediaObject_Release(IMediaObject
*iface
)
139 struct mp3_decoder
*This
= impl_from_IMediaObject(iface
);
140 return IUnknown_Release(This
->outer
);
143 static HRESULT WINAPI
MediaObject_GetStreamCount(IMediaObject
*iface
, DWORD
*input
, DWORD
*output
)
145 TRACE("iface %p, input %p, output %p.\n", iface
, input
, output
);
147 *input
= *output
= 1;
152 static HRESULT WINAPI
MediaObject_GetInputStreamInfo(IMediaObject
*iface
, DWORD index
, DWORD
*flags
)
154 TRACE("iface %p, index %lu, flags %p.\n", iface
, index
, flags
);
161 static HRESULT WINAPI
MediaObject_GetOutputStreamInfo(IMediaObject
*iface
, DWORD index
, DWORD
*flags
)
163 TRACE("iface %p, index %lu, flags %p.\n", iface
, index
, flags
);
170 static HRESULT WINAPI
MediaObject_GetInputType(IMediaObject
*iface
, DWORD index
, DWORD type_index
, DMO_MEDIA_TYPE
*type
)
172 TRACE("iface %p, index %lu, type_index %lu, type %p.\n", iface
, index
, type_index
, type
);
175 return DMO_E_NO_MORE_ITEMS
;
177 type
->majortype
= WMMEDIATYPE_Audio
;
178 type
->subtype
= WMMEDIASUBTYPE_MP3
;
179 type
->formattype
= GUID_NULL
;
182 type
->pbFormat
= NULL
;
187 static HRESULT WINAPI
MediaObject_GetOutputType(IMediaObject
*iface
, DWORD index
, DWORD type_index
, DMO_MEDIA_TYPE
*type
)
189 struct mp3_decoder
*dmo
= impl_from_IMediaObject(iface
);
190 const WAVEFORMATEX
*input_format
;
191 WAVEFORMATEX
*format
;
193 TRACE("iface %p, index %lu, type_index %lu, type %p.\n", iface
, index
, type_index
, type
);
195 if (!dmo
->intype_set
)
196 return DMO_E_TYPE_NOT_SET
;
198 input_format
= (WAVEFORMATEX
*)dmo
->intype
.pbFormat
;
200 if (type_index
>= (2 * input_format
->nChannels
))
201 return DMO_E_NO_MORE_ITEMS
;
203 type
->majortype
= WMMEDIATYPE_Audio
;
204 type
->subtype
= WMMEDIASUBTYPE_PCM
;
205 type
->formattype
= WMFORMAT_WaveFormatEx
;
207 type
->cbFormat
= sizeof(WAVEFORMATEX
);
208 if (!(type
->pbFormat
= CoTaskMemAlloc(sizeof(WAVEFORMATEX
))))
209 return E_OUTOFMEMORY
;
210 format
= (WAVEFORMATEX
*)type
->pbFormat
;
211 format
->wFormatTag
= WAVE_FORMAT_PCM
;
212 format
->nSamplesPerSec
= input_format
->nSamplesPerSec
;
213 format
->nChannels
= (type_index
/ 2) ? 1 : input_format
->nChannels
;
214 format
->wBitsPerSample
= (type_index
% 2) ? 8 : 16;
215 format
->nBlockAlign
= format
->nChannels
* format
->wBitsPerSample
/ 8;
216 format
->nAvgBytesPerSec
= format
->nSamplesPerSec
* format
->nBlockAlign
;
222 static HRESULT WINAPI
MediaObject_SetInputType(IMediaObject
*iface
, DWORD index
, const DMO_MEDIA_TYPE
*type
, DWORD flags
)
224 struct mp3_decoder
*dmo
= impl_from_IMediaObject(iface
);
226 TRACE("iface %p, index %lu, type %p, flags %#lx.\n", iface
, index
, type
, flags
);
228 if (flags
& DMO_SET_TYPEF_CLEAR
)
231 MoFreeMediaType(&dmo
->intype
);
232 dmo
->intype_set
= FALSE
;
236 if (!IsEqualGUID(&type
->majortype
, &WMMEDIATYPE_Audio
)
237 || !IsEqualGUID(&type
->subtype
, &WMMEDIASUBTYPE_MP3
)
238 || !IsEqualGUID(&type
->formattype
, &WMFORMAT_WaveFormatEx
))
239 return DMO_E_TYPE_NOT_ACCEPTED
;
241 if (!(flags
& DMO_SET_TYPEF_TEST_ONLY
))
244 MoFreeMediaType(&dmo
->intype
);
245 MoCopyMediaType(&dmo
->intype
, type
);
246 dmo
->intype_set
= TRUE
;
252 static HRESULT WINAPI
MediaObject_SetOutputType(IMediaObject
*iface
, DWORD index
, const DMO_MEDIA_TYPE
*type
, DWORD flags
)
254 struct mp3_decoder
*This
= impl_from_IMediaObject(iface
);
255 WAVEFORMATEX
*format
;
259 TRACE("(%p)->(%ld, %p, %#lx)\n", iface
, index
, type
, flags
);
261 if (flags
& DMO_SET_TYPEF_CLEAR
)
263 MoFreeMediaType(&This
->outtype
);
264 This
->outtype_set
= FALSE
;
268 if (!IsEqualGUID(&type
->formattype
, &WMFORMAT_WaveFormatEx
))
269 return DMO_E_TYPE_NOT_ACCEPTED
;
271 format
= (WAVEFORMATEX
*)type
->pbFormat
;
273 if (format
->wBitsPerSample
== 8)
274 enc
= MPG123_ENC_UNSIGNED_8
;
275 else if (format
->wBitsPerSample
== 16)
276 enc
= MPG123_ENC_SIGNED_16
;
279 ERR("Cannot decode to bit depth %u.\n", format
->wBitsPerSample
);
280 return DMO_E_TYPE_NOT_ACCEPTED
;
283 if (!(flags
& DMO_SET_TYPEF_TEST_ONLY
))
285 err
= mpg123_format(This
->mh
, format
->nSamplesPerSec
, format
->nChannels
, enc
);
286 if (err
!= MPG123_OK
)
288 ERR("Failed to set format: %u channels, %lu samples/sec, %u bits/sample.\n",
289 format
->nChannels
, format
->nSamplesPerSec
, format
->wBitsPerSample
);
290 return DMO_E_TYPE_NOT_ACCEPTED
;
292 MoCopyMediaType(&This
->outtype
, type
);
293 This
->outtype_set
= TRUE
;
299 static HRESULT WINAPI
MediaObject_GetInputCurrentType(IMediaObject
*iface
, DWORD index
, DMO_MEDIA_TYPE
*type
)
301 FIXME("(%p)->(%ld, %p) stub!\n", iface
, index
, type
);
306 static HRESULT WINAPI
MediaObject_GetOutputCurrentType(IMediaObject
*iface
, DWORD index
, DMO_MEDIA_TYPE
*type
)
308 FIXME("(%p)->(%ld, %p) stub!\n", iface
, index
, type
);
313 static HRESULT WINAPI
MediaObject_GetInputSizeInfo(IMediaObject
*iface
,
314 DWORD index
, DWORD
*size
, DWORD
*lookahead
, DWORD
*alignment
)
316 struct mp3_decoder
*dmo
= impl_from_IMediaObject(iface
);
318 TRACE("iface %p, index %lu, size %p, lookahead %p, alignment %p.\n", iface
, index
, size
, lookahead
, alignment
);
320 if (!dmo
->intype_set
|| !dmo
->outtype_set
)
321 return DMO_E_TYPE_NOT_SET
;
328 static HRESULT WINAPI
MediaObject_GetOutputSizeInfo(IMediaObject
*iface
, DWORD index
, DWORD
*size
, DWORD
*alignment
)
330 struct mp3_decoder
*dmo
= impl_from_IMediaObject(iface
);
332 TRACE("iface %p, index %lu, size %p, alignment %p.\n", iface
, index
, size
, alignment
);
334 if (!dmo
->intype_set
|| !dmo
->outtype_set
)
335 return DMO_E_TYPE_NOT_SET
;
337 *size
= 2 * 1152 * ((WAVEFORMATEX
*)dmo
->outtype
.pbFormat
)->wBitsPerSample
/ 8;
342 static HRESULT WINAPI
MediaObject_GetInputMaxLatency(IMediaObject
*iface
, DWORD index
, REFERENCE_TIME
*latency
)
344 FIXME("(%p)->(%ld, %p) stub!\n", iface
, index
, latency
);
349 static HRESULT WINAPI
MediaObject_SetInputMaxLatency(IMediaObject
*iface
, DWORD index
, REFERENCE_TIME latency
)
351 FIXME("(%p)->(%ld, %s) stub!\n", iface
, index
, wine_dbgstr_longlong(latency
));
356 static HRESULT WINAPI
MediaObject_Flush(IMediaObject
*iface
)
358 struct mp3_decoder
*dmo
= impl_from_IMediaObject(iface
);
360 TRACE("iface %p.\n", iface
);
363 IMediaBuffer_Release(dmo
->buffer
);
367 /* mpg123 doesn't give us a way to flush, so just close and reopen the feed. */
368 mpg123_close(dmo
->mh
);
369 mpg123_open_feed(dmo
->mh
);
374 static HRESULT WINAPI
MediaObject_Discontinuity(IMediaObject
*iface
, DWORD index
)
376 TRACE("iface %p.\n", iface
);
381 static HRESULT WINAPI
MediaObject_AllocateStreamingResources(IMediaObject
*iface
)
383 FIXME("(%p)->() stub!\n", iface
);
388 static HRESULT WINAPI
MediaObject_FreeStreamingResources(IMediaObject
*iface
)
390 FIXME("(%p)->() stub!\n", iface
);
395 static HRESULT WINAPI
MediaObject_GetInputStatus(IMediaObject
*iface
, DWORD index
, DWORD
*flags
)
397 FIXME("(%p)->(%ld, %p) stub!\n", iface
, index
, flags
);
402 static HRESULT WINAPI
MediaObject_ProcessInput(IMediaObject
*iface
, DWORD index
,
403 IMediaBuffer
*buffer
, DWORD flags
, REFERENCE_TIME timestamp
, REFERENCE_TIME timelength
)
405 struct mp3_decoder
*This
= impl_from_IMediaObject(iface
);
411 TRACE("(%p)->(%ld, %p, %#lx, %s, %s)\n", iface
, index
, buffer
, flags
,
412 wine_dbgstr_longlong(timestamp
), wine_dbgstr_longlong(timelength
));
416 ERR("Already have a buffer.\n");
417 return DMO_E_NOTACCEPTING
;
420 IMediaBuffer_AddRef(buffer
);
421 This
->buffer
= buffer
;
423 hr
= IMediaBuffer_GetBufferAndLength(buffer
, &data
, &len
);
427 err
= mpg123_feed(This
->mh
, data
, len
);
428 if (err
!= MPG123_OK
)
430 ERR("mpg123_feed() failed: %s\n", mpg123_strerror(This
->mh
));
437 static DWORD
get_framesize(DMO_MEDIA_TYPE
*type
)
439 WAVEFORMATEX
*format
= (WAVEFORMATEX
*)type
->pbFormat
;
440 return 1152 * format
->nBlockAlign
;
443 static REFERENCE_TIME
get_frametime(DMO_MEDIA_TYPE
*type
)
445 WAVEFORMATEX
*format
= (WAVEFORMATEX
*)type
->pbFormat
;
446 return (REFERENCE_TIME
) 10000000 * 1152 / format
->nSamplesPerSec
;
449 static HRESULT WINAPI
MediaObject_ProcessOutput(IMediaObject
*iface
, DWORD flags
, DWORD count
, DMO_OUTPUT_DATA_BUFFER
*buffers
, DWORD
*status
)
451 struct mp3_decoder
*This
= impl_from_IMediaObject(iface
);
452 REFERENCE_TIME time
= 0, frametime
;
453 DWORD len
, maxlen
, framesize
;
460 TRACE("(%p)->(%#lx, %ld, %p, %p)\n", iface
, flags
, count
, buffers
, status
);
463 FIXME("Multiple buffers not handled.\n");
465 buffers
[0].dwStatus
= 0;
467 if (!buffers
[0].pBuffer
)
469 while ((err
= mpg123_read(This
->mh
, NULL
, 0, &written
)) == MPG123_NEW_FORMAT
);
470 if (err
== MPG123_NEED_MORE
)
472 else if (err
== MPG123_ERR
)
473 ERR("mpg123_read() failed: %s\n", mpg123_strerror(This
->mh
));
474 else if (err
!= MPG123_OK
)
475 ERR("mpg123_read() returned %d\n", err
);
477 buffers
[0].dwStatus
= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE
;
484 buffers
[0].dwStatus
|= DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT
;
486 hr
= IMediaBuffer_GetBufferAndLength(buffers
[0].pBuffer
, &data
, &len
);
487 if (FAILED(hr
)) return hr
;
489 hr
= IMediaBuffer_GetMaxLength(buffers
[0].pBuffer
, &maxlen
);
490 if (FAILED(hr
)) return hr
;
492 framesize
= get_framesize(&This
->outtype
);
493 frametime
= get_frametime(&This
->outtype
);
497 if (maxlen
- len
< framesize
)
499 buffers
[0].dwStatus
|= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE
;
503 while ((err
= mpg123_read(This
->mh
, data
+ len
, framesize
, &written
)) == MPG123_NEW_FORMAT
);
504 if (err
== MPG123_NEED_MORE
)
506 IMediaBuffer_Release(This
->buffer
);
510 else if (err
== MPG123_ERR
)
511 ERR("mpg123_read() failed: %s\n", mpg123_strerror(This
->mh
));
512 else if (err
!= MPG123_OK
)
513 ERR("mpg123_read() returned %d\n", err
);
514 if (written
< framesize
)
515 ERR("short write: %Id/%lu\n", written
, framesize
);
520 hr
= IMediaBuffer_SetLength(buffers
[0].pBuffer
, len
);
521 if (FAILED(hr
)) return hr
;
528 buffers
[0].dwStatus
|= (DMO_OUTPUT_DATA_BUFFERF_TIME
| DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH
);
529 buffers
[0].rtTimelength
= time
;
530 buffers
[0].rtTimestamp
= This
->timestamp
;
531 This
->timestamp
+= time
;
537 static HRESULT WINAPI
MediaObject_Lock(IMediaObject
*iface
, LONG lock
)
539 FIXME("(%p)->(%ld) stub!\n", iface
, lock
);
544 static const IMediaObjectVtbl MediaObject_vtbl
= {
545 MediaObject_QueryInterface
,
548 MediaObject_GetStreamCount
,
549 MediaObject_GetInputStreamInfo
,
550 MediaObject_GetOutputStreamInfo
,
551 MediaObject_GetInputType
,
552 MediaObject_GetOutputType
,
553 MediaObject_SetInputType
,
554 MediaObject_SetOutputType
,
555 MediaObject_GetInputCurrentType
,
556 MediaObject_GetOutputCurrentType
,
557 MediaObject_GetInputSizeInfo
,
558 MediaObject_GetOutputSizeInfo
,
559 MediaObject_GetInputMaxLatency
,
560 MediaObject_SetInputMaxLatency
,
562 MediaObject_Discontinuity
,
563 MediaObject_AllocateStreamingResources
,
564 MediaObject_FreeStreamingResources
,
565 MediaObject_GetInputStatus
,
566 MediaObject_ProcessInput
,
567 MediaObject_ProcessOutput
,
571 static HRESULT
create_mp3_decoder(IUnknown
*outer
, REFIID iid
, void **obj
)
573 struct mp3_decoder
*This
;
577 if (!(This
= calloc(1, sizeof(*This
))))
578 return E_OUTOFMEMORY
;
580 This
->IUnknown_inner
.lpVtbl
= &Unknown_vtbl
;
581 This
->IMediaObject_iface
.lpVtbl
= &MediaObject_vtbl
;
583 This
->outer
= outer
? outer
: &This
->IUnknown_inner
;
586 This
->mh
= mpg123_new(NULL
, &err
);
587 mpg123_open_feed(This
->mh
);
588 mpg123_format_none(This
->mh
);
590 hr
= IUnknown_QueryInterface(&This
->IUnknown_inner
, iid
, obj
);
591 IUnknown_Release(&This
->IUnknown_inner
);
595 static HRESULT WINAPI
ClassFactory_QueryInterface(IClassFactory
*iface
, REFIID iid
, void **obj
)
597 TRACE("(%p, %s, %p)\n", iface
, debugstr_guid(iid
), obj
);
599 if (IsEqualGUID(&IID_IUnknown
, iid
) ||
600 IsEqualGUID(&IID_IClassFactory
, iid
))
602 IClassFactory_AddRef(iface
);
608 WARN("no interface for %s\n", debugstr_guid(iid
));
609 return E_NOINTERFACE
;
612 static ULONG WINAPI
ClassFactory_AddRef(IClassFactory
*iface
)
617 static ULONG WINAPI
ClassFactory_Release(IClassFactory
*iface
)
622 static HRESULT WINAPI
ClassFactory_CreateInstance(IClassFactory
*iface
, IUnknown
*outer
, REFIID iid
, void **obj
)
624 TRACE("(%p, %s, %p)\n", outer
, debugstr_guid(iid
), obj
);
626 if (outer
&& !IsEqualGUID(iid
, &IID_IUnknown
))
629 return E_NOINTERFACE
;
632 return create_mp3_decoder(outer
, iid
, obj
);
635 static HRESULT WINAPI
ClassFactory_LockServer(IClassFactory
*iface
, BOOL lock
)
637 FIXME("(%d) stub\n", lock
);
641 static const IClassFactoryVtbl classfactory_vtbl
= {
642 ClassFactory_QueryInterface
,
644 ClassFactory_Release
,
645 ClassFactory_CreateInstance
,
646 ClassFactory_LockServer
649 static IClassFactory mp3_decoder_cf
= { &classfactory_vtbl
};
651 /*************************************************************************
652 * DllGetClassObject (DSDMO.@)
654 HRESULT WINAPI
DllGetClassObject(REFCLSID clsid
, REFIID iid
, void **obj
)
656 TRACE("%s, %s, %p\n", debugstr_guid(clsid
), debugstr_guid(iid
), obj
);
658 if (IsEqualGUID(clsid
, &CLSID_CMP3DecMediaObject
))
659 return IClassFactory_QueryInterface(&mp3_decoder_cf
, iid
, obj
);
661 FIXME("class %s not available\n", debugstr_guid(clsid
));
662 return CLASS_E_CLASSNOTAVAILABLE
;
665 /***********************************************************************
666 * DllRegisterServer (DSDMO.@)
668 HRESULT WINAPI
DllRegisterServer(void)
670 DMO_PARTIAL_MEDIATYPE in
, out
;
673 in
.type
= WMMEDIATYPE_Audio
;
674 in
.subtype
= WMMEDIASUBTYPE_MP3
;
675 out
.type
= WMMEDIATYPE_Audio
;
676 out
.subtype
= WMMEDIASUBTYPE_PCM
;
677 hr
= DMORegister(L
"MP3 Decoder DMO", &CLSID_CMP3DecMediaObject
, &DMOCATEGORY_AUDIO_DECODER
,
679 if (FAILED(hr
)) return hr
;
681 return __wine_register_resources();
684 /***********************************************************************
685 * DllUnregisterServer (DSDMO.@)
687 HRESULT WINAPI
DllUnregisterServer(void)
691 hr
= DMOUnregister(&CLSID_CMP3DecMediaObject
, &DMOCATEGORY_AUDIO_DECODER
);
692 if (FAILED(hr
)) return hr
;
694 return __wine_unregister_resources();