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
);
46 IUnknown IUnknown_inner
;
47 IMediaObject IMediaObject_iface
;
52 DMO_MEDIA_TYPE intype
, outtype
;
53 BOOL intype_set
, outtype_set
;
56 REFERENCE_TIME timestamp
;
59 static inline struct mp3_decoder
*impl_from_IUnknown(IUnknown
*iface
)
61 return CONTAINING_RECORD(iface
, struct mp3_decoder
, IUnknown_inner
);
64 static HRESULT WINAPI
Unknown_QueryInterface(IUnknown
*iface
, REFIID iid
, void **obj
)
66 struct mp3_decoder
*This
= impl_from_IUnknown(iface
);
68 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(iid
), obj
);
70 if (IsEqualGUID(iid
, &IID_IUnknown
))
71 *obj
= &This
->IUnknown_inner
;
72 else if (IsEqualGUID(iid
, &IID_IMediaObject
))
73 *obj
= &This
->IMediaObject_iface
;
76 FIXME("no interface for %s\n", debugstr_guid(iid
));
81 IUnknown_AddRef((IUnknown
*)*obj
);
85 static ULONG WINAPI
Unknown_AddRef(IUnknown
*iface
)
87 struct mp3_decoder
*This
= impl_from_IUnknown(iface
);
88 ULONG refcount
= InterlockedIncrement(&This
->ref
);
90 TRACE("(%p) AddRef from %ld\n", This
, refcount
- 1);
95 static ULONG WINAPI
Unknown_Release(IUnknown
*iface
)
97 struct mp3_decoder
*This
= impl_from_IUnknown(iface
);
98 ULONG refcount
= InterlockedDecrement(&This
->ref
);
100 TRACE("(%p) Release from %ld\n", This
, refcount
+ 1);
105 IMediaBuffer_Release(This
->buffer
);
106 if (This
->intype_set
)
107 MoFreeMediaType(&This
->intype
);
108 MoFreeMediaType(&This
->outtype
);
109 mpg123_delete(This
->mh
);
115 static const IUnknownVtbl Unknown_vtbl
= {
116 Unknown_QueryInterface
,
121 static inline struct mp3_decoder
*impl_from_IMediaObject(IMediaObject
*iface
)
123 return CONTAINING_RECORD(iface
, struct mp3_decoder
, IMediaObject_iface
);
126 static HRESULT WINAPI
MediaObject_QueryInterface(IMediaObject
*iface
, REFIID iid
, void **obj
)
128 struct mp3_decoder
*This
= impl_from_IMediaObject(iface
);
129 return IUnknown_QueryInterface(This
->outer
, iid
, obj
);
132 static ULONG WINAPI
MediaObject_AddRef(IMediaObject
*iface
)
134 struct mp3_decoder
*This
= impl_from_IMediaObject(iface
);
135 return IUnknown_AddRef(This
->outer
);
138 static ULONG WINAPI
MediaObject_Release(IMediaObject
*iface
)
140 struct mp3_decoder
*This
= impl_from_IMediaObject(iface
);
141 return IUnknown_Release(This
->outer
);
144 static HRESULT WINAPI
MediaObject_GetStreamCount(IMediaObject
*iface
, DWORD
*input
, DWORD
*output
)
146 TRACE("iface %p, input %p, output %p.\n", iface
, input
, output
);
148 *input
= *output
= 1;
153 static HRESULT WINAPI
MediaObject_GetInputStreamInfo(IMediaObject
*iface
, DWORD index
, DWORD
*flags
)
155 TRACE("iface %p, index %lu, flags %p.\n", iface
, index
, flags
);
162 static HRESULT WINAPI
MediaObject_GetOutputStreamInfo(IMediaObject
*iface
, DWORD index
, DWORD
*flags
)
164 TRACE("iface %p, index %lu, flags %p.\n", iface
, index
, flags
);
171 static HRESULT WINAPI
MediaObject_GetInputType(IMediaObject
*iface
, DWORD index
, DWORD type_index
, DMO_MEDIA_TYPE
*type
)
173 TRACE("iface %p, index %lu, type_index %lu, type %p.\n", iface
, index
, type_index
, type
);
176 return DMO_E_NO_MORE_ITEMS
;
178 type
->majortype
= WMMEDIATYPE_Audio
;
179 type
->subtype
= WMMEDIASUBTYPE_MP3
;
180 type
->formattype
= GUID_NULL
;
183 type
->pbFormat
= NULL
;
188 static HRESULT WINAPI
MediaObject_GetOutputType(IMediaObject
*iface
, DWORD index
, DWORD type_index
, DMO_MEDIA_TYPE
*type
)
190 struct mp3_decoder
*dmo
= impl_from_IMediaObject(iface
);
191 const WAVEFORMATEX
*input_format
;
192 WAVEFORMATEX
*format
;
194 TRACE("iface %p, index %lu, type_index %lu, type %p.\n", iface
, index
, type_index
, type
);
196 if (!dmo
->intype_set
)
197 return DMO_E_TYPE_NOT_SET
;
199 input_format
= (WAVEFORMATEX
*)dmo
->intype
.pbFormat
;
201 if (type_index
>= (2 * input_format
->nChannels
))
202 return DMO_E_NO_MORE_ITEMS
;
204 type
->majortype
= WMMEDIATYPE_Audio
;
205 type
->subtype
= WMMEDIASUBTYPE_PCM
;
206 type
->formattype
= WMFORMAT_WaveFormatEx
;
208 type
->cbFormat
= sizeof(WAVEFORMATEX
);
209 if (!(type
->pbFormat
= CoTaskMemAlloc(sizeof(WAVEFORMATEX
))))
210 return E_OUTOFMEMORY
;
211 format
= (WAVEFORMATEX
*)type
->pbFormat
;
212 format
->wFormatTag
= WAVE_FORMAT_PCM
;
213 format
->nSamplesPerSec
= input_format
->nSamplesPerSec
;
214 format
->nChannels
= (type_index
/ 2) ? 1 : input_format
->nChannels
;
215 format
->wBitsPerSample
= (type_index
% 2) ? 8 : 16;
216 format
->nBlockAlign
= format
->nChannels
* format
->wBitsPerSample
/ 8;
217 format
->nAvgBytesPerSec
= format
->nSamplesPerSec
* format
->nBlockAlign
;
223 static HRESULT WINAPI
MediaObject_SetInputType(IMediaObject
*iface
, DWORD index
, const DMO_MEDIA_TYPE
*type
, DWORD flags
)
225 struct mp3_decoder
*dmo
= impl_from_IMediaObject(iface
);
227 TRACE("iface %p, index %lu, type %p, flags %#lx.\n", iface
, index
, type
, flags
);
229 if (flags
& DMO_SET_TYPEF_CLEAR
)
232 MoFreeMediaType(&dmo
->intype
);
233 dmo
->intype_set
= FALSE
;
237 if (!IsEqualGUID(&type
->majortype
, &WMMEDIATYPE_Audio
)
238 || !IsEqualGUID(&type
->subtype
, &WMMEDIASUBTYPE_MP3
)
239 || !IsEqualGUID(&type
->formattype
, &WMFORMAT_WaveFormatEx
))
240 return DMO_E_TYPE_NOT_ACCEPTED
;
242 if (!(flags
& DMO_SET_TYPEF_TEST_ONLY
))
245 MoFreeMediaType(&dmo
->intype
);
246 MoCopyMediaType(&dmo
->intype
, type
);
247 dmo
->intype_set
= TRUE
;
253 static HRESULT WINAPI
MediaObject_SetOutputType(IMediaObject
*iface
, DWORD index
, const DMO_MEDIA_TYPE
*type
, DWORD flags
)
255 struct mp3_decoder
*This
= impl_from_IMediaObject(iface
);
256 WAVEFORMATEX
*format
;
260 TRACE("(%p)->(%ld, %p, %#lx)\n", iface
, index
, type
, flags
);
262 if (flags
& DMO_SET_TYPEF_CLEAR
)
264 MoFreeMediaType(&This
->outtype
);
265 This
->outtype_set
= FALSE
;
269 if (!IsEqualGUID(&type
->formattype
, &WMFORMAT_WaveFormatEx
))
270 return DMO_E_TYPE_NOT_ACCEPTED
;
272 format
= (WAVEFORMATEX
*)type
->pbFormat
;
274 if (format
->wBitsPerSample
== 8)
275 enc
= MPG123_ENC_UNSIGNED_8
;
276 else if (format
->wBitsPerSample
== 16)
277 enc
= MPG123_ENC_SIGNED_16
;
280 ERR("Cannot decode to bit depth %u.\n", format
->wBitsPerSample
);
281 return DMO_E_TYPE_NOT_ACCEPTED
;
284 if (!(flags
& DMO_SET_TYPEF_TEST_ONLY
))
286 err
= mpg123_format(This
->mh
, format
->nSamplesPerSec
, format
->nChannels
, enc
);
287 if (err
!= MPG123_OK
)
289 ERR("Failed to set format: %u channels, %lu samples/sec, %u bits/sample.\n",
290 format
->nChannels
, format
->nSamplesPerSec
, format
->wBitsPerSample
);
291 return DMO_E_TYPE_NOT_ACCEPTED
;
293 MoCopyMediaType(&This
->outtype
, type
);
294 This
->outtype_set
= TRUE
;
300 static HRESULT WINAPI
MediaObject_GetInputCurrentType(IMediaObject
*iface
, DWORD index
, DMO_MEDIA_TYPE
*type
)
302 FIXME("(%p)->(%ld, %p) stub!\n", iface
, index
, type
);
307 static HRESULT WINAPI
MediaObject_GetOutputCurrentType(IMediaObject
*iface
, DWORD index
, DMO_MEDIA_TYPE
*type
)
309 FIXME("(%p)->(%ld, %p) stub!\n", iface
, index
, type
);
314 static HRESULT WINAPI
MediaObject_GetInputSizeInfo(IMediaObject
*iface
,
315 DWORD index
, DWORD
*size
, DWORD
*lookahead
, DWORD
*alignment
)
317 struct mp3_decoder
*dmo
= impl_from_IMediaObject(iface
);
319 TRACE("iface %p, index %lu, size %p, lookahead %p, alignment %p.\n", iface
, index
, size
, lookahead
, alignment
);
321 if (!dmo
->intype_set
|| !dmo
->outtype_set
)
322 return DMO_E_TYPE_NOT_SET
;
329 static HRESULT WINAPI
MediaObject_GetOutputSizeInfo(IMediaObject
*iface
, DWORD index
, DWORD
*size
, DWORD
*alignment
)
331 struct mp3_decoder
*dmo
= impl_from_IMediaObject(iface
);
333 TRACE("iface %p, index %lu, size %p, alignment %p.\n", iface
, index
, size
, alignment
);
335 if (!dmo
->intype_set
|| !dmo
->outtype_set
)
336 return DMO_E_TYPE_NOT_SET
;
338 *size
= 2 * 1152 * ((WAVEFORMATEX
*)dmo
->outtype
.pbFormat
)->wBitsPerSample
/ 8;
343 static HRESULT WINAPI
MediaObject_GetInputMaxLatency(IMediaObject
*iface
, DWORD index
, REFERENCE_TIME
*latency
)
345 FIXME("(%p)->(%ld, %p) stub!\n", iface
, index
, latency
);
350 static HRESULT WINAPI
MediaObject_SetInputMaxLatency(IMediaObject
*iface
, DWORD index
, REFERENCE_TIME latency
)
352 FIXME("(%p)->(%ld, %s) stub!\n", iface
, index
, wine_dbgstr_longlong(latency
));
357 static HRESULT WINAPI
MediaObject_Flush(IMediaObject
*iface
)
359 struct mp3_decoder
*dmo
= impl_from_IMediaObject(iface
);
361 TRACE("iface %p.\n", iface
);
364 IMediaBuffer_Release(dmo
->buffer
);
368 /* mpg123 doesn't give us a way to flush, so just close and reopen the feed. */
369 mpg123_close(dmo
->mh
);
370 mpg123_open_feed(dmo
->mh
);
375 static HRESULT WINAPI
MediaObject_Discontinuity(IMediaObject
*iface
, DWORD index
)
377 TRACE("iface %p.\n", iface
);
382 static HRESULT WINAPI
MediaObject_AllocateStreamingResources(IMediaObject
*iface
)
384 FIXME("(%p)->() stub!\n", iface
);
389 static HRESULT WINAPI
MediaObject_FreeStreamingResources(IMediaObject
*iface
)
391 FIXME("(%p)->() stub!\n", iface
);
396 static HRESULT WINAPI
MediaObject_GetInputStatus(IMediaObject
*iface
, DWORD index
, DWORD
*flags
)
398 FIXME("(%p)->(%ld, %p) stub!\n", iface
, index
, flags
);
403 static HRESULT WINAPI
MediaObject_ProcessInput(IMediaObject
*iface
, DWORD index
,
404 IMediaBuffer
*buffer
, DWORD flags
, REFERENCE_TIME timestamp
, REFERENCE_TIME timelength
)
406 struct mp3_decoder
*This
= impl_from_IMediaObject(iface
);
412 TRACE("(%p)->(%ld, %p, %#lx, %s, %s)\n", iface
, index
, buffer
, flags
,
413 wine_dbgstr_longlong(timestamp
), wine_dbgstr_longlong(timelength
));
417 ERR("Already have a buffer.\n");
418 return DMO_E_NOTACCEPTING
;
421 IMediaBuffer_AddRef(buffer
);
422 This
->buffer
= buffer
;
424 hr
= IMediaBuffer_GetBufferAndLength(buffer
, &data
, &len
);
428 err
= mpg123_feed(This
->mh
, data
, len
);
429 if (err
!= MPG123_OK
)
431 ERR("mpg123_feed() failed: %s\n", mpg123_strerror(This
->mh
));
438 static DWORD
get_framesize(DMO_MEDIA_TYPE
*type
)
440 WAVEFORMATEX
*format
= (WAVEFORMATEX
*)type
->pbFormat
;
441 return 1152 * format
->nBlockAlign
;
444 static REFERENCE_TIME
get_frametime(DMO_MEDIA_TYPE
*type
)
446 WAVEFORMATEX
*format
= (WAVEFORMATEX
*)type
->pbFormat
;
447 return (REFERENCE_TIME
) 10000000 * 1152 / format
->nSamplesPerSec
;
450 static HRESULT WINAPI
MediaObject_ProcessOutput(IMediaObject
*iface
, DWORD flags
, DWORD count
, DMO_OUTPUT_DATA_BUFFER
*buffers
, DWORD
*status
)
452 struct mp3_decoder
*This
= impl_from_IMediaObject(iface
);
453 REFERENCE_TIME time
= 0, frametime
;
454 DWORD len
, maxlen
, framesize
;
461 TRACE("(%p)->(%#lx, %ld, %p, %p)\n", iface
, flags
, count
, buffers
, status
);
464 FIXME("Multiple buffers not handled.\n");
466 buffers
[0].dwStatus
= 0;
468 if (!buffers
[0].pBuffer
)
470 while ((err
= mpg123_read(This
->mh
, NULL
, 0, &written
)) == MPG123_NEW_FORMAT
);
471 if (err
== MPG123_NEED_MORE
)
473 else if (err
== MPG123_ERR
)
474 ERR("mpg123_read() failed: %s\n", mpg123_strerror(This
->mh
));
475 else if (err
!= MPG123_OK
)
476 ERR("mpg123_read() returned %d\n", err
);
478 buffers
[0].dwStatus
= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE
;
485 buffers
[0].dwStatus
|= DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT
;
487 hr
= IMediaBuffer_GetBufferAndLength(buffers
[0].pBuffer
, &data
, &len
);
488 if (FAILED(hr
)) return hr
;
490 hr
= IMediaBuffer_GetMaxLength(buffers
[0].pBuffer
, &maxlen
);
491 if (FAILED(hr
)) return hr
;
493 framesize
= get_framesize(&This
->outtype
);
494 frametime
= get_frametime(&This
->outtype
);
498 if (maxlen
- len
< framesize
)
500 buffers
[0].dwStatus
|= DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE
;
504 while ((err
= mpg123_read(This
->mh
, data
+ len
, framesize
, &written
)) == MPG123_NEW_FORMAT
);
505 if (err
== MPG123_NEED_MORE
)
507 IMediaBuffer_Release(This
->buffer
);
511 else if (err
== MPG123_ERR
)
512 ERR("mpg123_read() failed: %s\n", mpg123_strerror(This
->mh
));
513 else if (err
!= MPG123_OK
)
514 ERR("mpg123_read() returned %d\n", err
);
515 if (written
< framesize
)
516 ERR("short write: %Id/%lu\n", written
, framesize
);
521 hr
= IMediaBuffer_SetLength(buffers
[0].pBuffer
, len
);
522 if (FAILED(hr
)) return hr
;
529 buffers
[0].dwStatus
|= (DMO_OUTPUT_DATA_BUFFERF_TIME
| DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH
);
530 buffers
[0].rtTimelength
= time
;
531 buffers
[0].rtTimestamp
= This
->timestamp
;
532 This
->timestamp
+= time
;
538 static HRESULT WINAPI
MediaObject_Lock(IMediaObject
*iface
, LONG lock
)
540 FIXME("(%p)->(%ld) stub!\n", iface
, lock
);
545 static const IMediaObjectVtbl MediaObject_vtbl
= {
546 MediaObject_QueryInterface
,
549 MediaObject_GetStreamCount
,
550 MediaObject_GetInputStreamInfo
,
551 MediaObject_GetOutputStreamInfo
,
552 MediaObject_GetInputType
,
553 MediaObject_GetOutputType
,
554 MediaObject_SetInputType
,
555 MediaObject_SetOutputType
,
556 MediaObject_GetInputCurrentType
,
557 MediaObject_GetOutputCurrentType
,
558 MediaObject_GetInputSizeInfo
,
559 MediaObject_GetOutputSizeInfo
,
560 MediaObject_GetInputMaxLatency
,
561 MediaObject_SetInputMaxLatency
,
563 MediaObject_Discontinuity
,
564 MediaObject_AllocateStreamingResources
,
565 MediaObject_FreeStreamingResources
,
566 MediaObject_GetInputStatus
,
567 MediaObject_ProcessInput
,
568 MediaObject_ProcessOutput
,
572 static HRESULT
create_mp3_decoder(IUnknown
*outer
, REFIID iid
, void **obj
)
574 struct mp3_decoder
*This
;
578 if (!(This
= heap_alloc_zero(sizeof(*This
))))
579 return E_OUTOFMEMORY
;
581 This
->IUnknown_inner
.lpVtbl
= &Unknown_vtbl
;
582 This
->IMediaObject_iface
.lpVtbl
= &MediaObject_vtbl
;
584 This
->outer
= outer
? outer
: &This
->IUnknown_inner
;
587 This
->mh
= mpg123_new(NULL
, &err
);
588 mpg123_open_feed(This
->mh
);
589 mpg123_format_none(This
->mh
);
591 hr
= IUnknown_QueryInterface(&This
->IUnknown_inner
, iid
, obj
);
592 IUnknown_Release(&This
->IUnknown_inner
);
596 static HRESULT WINAPI
ClassFactory_QueryInterface(IClassFactory
*iface
, REFIID iid
, void **obj
)
598 TRACE("(%p, %s, %p)\n", iface
, debugstr_guid(iid
), obj
);
600 if (IsEqualGUID(&IID_IUnknown
, iid
) ||
601 IsEqualGUID(&IID_IClassFactory
, iid
))
603 IClassFactory_AddRef(iface
);
609 WARN("no interface for %s\n", debugstr_guid(iid
));
610 return E_NOINTERFACE
;
613 static ULONG WINAPI
ClassFactory_AddRef(IClassFactory
*iface
)
618 static ULONG WINAPI
ClassFactory_Release(IClassFactory
*iface
)
623 static HRESULT WINAPI
ClassFactory_CreateInstance(IClassFactory
*iface
, IUnknown
*outer
, REFIID iid
, void **obj
)
625 TRACE("(%p, %s, %p)\n", outer
, debugstr_guid(iid
), obj
);
627 if (outer
&& !IsEqualGUID(iid
, &IID_IUnknown
))
630 return E_NOINTERFACE
;
633 return create_mp3_decoder(outer
, iid
, obj
);
636 static HRESULT WINAPI
ClassFactory_LockServer(IClassFactory
*iface
, BOOL lock
)
638 FIXME("(%d) stub\n", lock
);
642 static const IClassFactoryVtbl classfactory_vtbl
= {
643 ClassFactory_QueryInterface
,
645 ClassFactory_Release
,
646 ClassFactory_CreateInstance
,
647 ClassFactory_LockServer
650 static IClassFactory mp3_decoder_cf
= { &classfactory_vtbl
};
652 /*************************************************************************
653 * DllGetClassObject (DSDMO.@)
655 HRESULT WINAPI
DllGetClassObject(REFCLSID clsid
, REFIID iid
, void **obj
)
657 TRACE("%s, %s, %p\n", debugstr_guid(clsid
), debugstr_guid(iid
), obj
);
659 if (IsEqualGUID(clsid
, &CLSID_CMP3DecMediaObject
))
660 return IClassFactory_QueryInterface(&mp3_decoder_cf
, iid
, obj
);
662 FIXME("class %s not available\n", debugstr_guid(clsid
));
663 return CLASS_E_CLASSNOTAVAILABLE
;
666 /***********************************************************************
667 * DllRegisterServer (DSDMO.@)
669 HRESULT WINAPI
DllRegisterServer(void)
671 DMO_PARTIAL_MEDIATYPE in
, out
;
674 in
.type
= WMMEDIATYPE_Audio
;
675 in
.subtype
= WMMEDIASUBTYPE_MP3
;
676 out
.type
= WMMEDIATYPE_Audio
;
677 out
.subtype
= WMMEDIASUBTYPE_PCM
;
678 hr
= DMORegister(L
"MP3 Decoder DMO", &CLSID_CMP3DecMediaObject
, &DMOCATEGORY_AUDIO_DECODER
,
680 if (FAILED(hr
)) return hr
;
682 return __wine_register_resources();
685 /***********************************************************************
686 * DllUnregisterServer (DSDMO.@)
688 HRESULT WINAPI
DllUnregisterServer(void)
692 hr
= DMOUnregister(&CLSID_CMP3DecMediaObject
, &DMOCATEGORY_AUDIO_DECODER
);
693 if (FAILED(hr
)) return hr
;
695 return __wine_unregister_resources();