Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/libs/faudio/src/FAudio_platform_win32_wmadec.c
4389 views
1
/* FAudio WMADEC implementation over Win32 MF */
2
3
#ifdef HAVE_WMADEC
4
5
#include "FAudio_internal.h"
6
7
#include <stddef.h>
8
9
#define COBJMACROS
10
#include <windows.h>
11
#include <mfidl.h>
12
#include <mfapi.h>
13
#include <mferror.h>
14
15
#include <initguid.h>
16
17
DEFINE_GUID(CLSID_CWMADecMediaObject, 0x2eeb4adf, 0x4578, 0x4d10, 0xbc, 0xa7, 0xbb, 0x95, 0x5f, 0x56, 0x32, 0x0a);
18
19
DEFINE_MEDIATYPE_GUID(MFAudioFormat_XMAudio2, FAUDIO_FORMAT_XMAUDIO2);
20
21
struct FAudioWMADEC
22
{
23
IMFTransform *decoder;
24
IMFSample *output_sample;
25
26
char *output_buf;
27
size_t output_pos;
28
size_t output_size;
29
size_t input_pos;
30
size_t input_size;
31
};
32
33
static HRESULT FAudio_WMAMF_ProcessInput(
34
FAudioVoice *voice,
35
FAudioBuffer *buffer
36
) {
37
struct FAudioWMADEC *impl = voice->src.wmadec;
38
IMFMediaBuffer *media_buffer;
39
IMFSample *sample;
40
DWORD copy_size;
41
BYTE *copy_buf;
42
HRESULT hr;
43
44
copy_size = (DWORD)min(buffer->AudioBytes - impl->input_pos, impl->input_size);
45
if (!copy_size) return S_FALSE;
46
LOG_INFO(voice->audio, "pushing %lx bytes at %Ix", copy_size, impl->input_pos);
47
48
hr = MFCreateSample(&sample);
49
FAudio_assert(!FAILED(hr) && "Failed to create sample!");
50
hr = MFCreateMemoryBuffer(copy_size, &media_buffer);
51
FAudio_assert(!FAILED(hr) && "Failed to create buffer!");
52
hr = IMFMediaBuffer_SetCurrentLength(media_buffer, copy_size);
53
FAudio_assert(!FAILED(hr) && "Failed to set buffer length!");
54
hr = IMFMediaBuffer_Lock(
55
media_buffer,
56
&copy_buf,
57
NULL,
58
&copy_size
59
);
60
FAudio_assert(!FAILED(hr) && "Failed to lock buffer bytes!");
61
FAudio_memcpy(copy_buf, buffer->pAudioData + impl->input_pos, copy_size);
62
hr = IMFMediaBuffer_Unlock(media_buffer);
63
FAudio_assert(!FAILED(hr) && "Failed to unlock buffer bytes!");
64
65
hr = IMFSample_AddBuffer(sample, media_buffer);
66
FAudio_assert(!FAILED(hr) && "Failed to buffer to sample!");
67
IMFMediaBuffer_Release(media_buffer);
68
69
hr = IMFTransform_ProcessInput(impl->decoder, 0, sample, 0);
70
IMFSample_Release(sample);
71
if (hr == MF_E_NOTACCEPTING) return S_OK;
72
if (FAILED(hr))
73
{
74
LOG_ERROR(voice->audio, "IMFTransform_ProcessInput returned %#lx", hr);
75
return hr;
76
}
77
78
impl->input_pos += copy_size;
79
return S_OK;
80
};
81
82
static HRESULT FAudio_WMAMF_ProcessOutput(
83
FAudioVoice *voice,
84
FAudioBuffer *buffer
85
) {
86
struct FAudioWMADEC *impl = voice->src.wmadec;
87
MFT_OUTPUT_DATA_BUFFER output;
88
IMFMediaBuffer *media_buffer;
89
DWORD status, copy_size;
90
BYTE *copy_buf;
91
HRESULT hr;
92
93
while (1)
94
{
95
FAudio_memset(&output, 0, sizeof(output));
96
output.pSample = impl->output_sample;
97
hr = IMFTransform_ProcessOutput(impl->decoder, 0, 1, &output, &status);
98
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) return S_FALSE;
99
if (FAILED(hr))
100
{
101
LOG_ERROR(voice->audio, "IMFTransform_ProcessInput returned %#lx", hr);
102
return hr;
103
}
104
105
if (output.dwStatus & MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE) continue;
106
107
hr = IMFSample_ConvertToContiguousBuffer(
108
output.pSample,
109
&media_buffer
110
);
111
FAudio_assert(!FAILED(hr) && "Failed to get sample buffer!");
112
hr = IMFMediaBuffer_Lock(
113
media_buffer,
114
&copy_buf,
115
NULL,
116
&copy_size
117
);
118
FAudio_assert(!FAILED(hr) && "Failed to lock buffer bytes!");
119
if (impl->output_pos + copy_size > impl->output_size)
120
{
121
impl->output_size = max(
122
impl->output_pos + copy_size,
123
impl->output_size * 3 / 2
124
);
125
impl->output_buf = voice->audio->pRealloc(
126
impl->output_buf,
127
impl->output_size
128
);
129
FAudio_assert(impl->output_buf && "Failed to resize output buffer!");
130
}
131
FAudio_memcpy(impl->output_buf + impl->output_pos, copy_buf, copy_size);
132
impl->output_pos += copy_size;
133
LOG_INFO(voice->audio, "pulled %lx bytes at %Ix", copy_size, impl->output_pos);
134
hr = IMFMediaBuffer_Unlock(media_buffer);
135
FAudio_assert(!FAILED(hr) && "Failed to unlock buffer bytes!");
136
137
IMFMediaBuffer_Release(media_buffer);
138
if (!impl->output_sample) IMFSample_Release(output.pSample);
139
}
140
141
return S_OK;
142
};
143
144
static void FAudio_INTERNAL_DecodeWMAMF(
145
FAudioVoice *voice,
146
FAudioBuffer *buffer,
147
float *decodeCache,
148
uint32_t samples
149
) {
150
const FAudioWaveFormatExtensible *wfx = (FAudioWaveFormatExtensible *)voice->src.format;
151
size_t samples_pos, samples_size, copy_size = 0;
152
struct FAudioWMADEC *impl = voice->src.wmadec;
153
HRESULT hr;
154
155
LOG_FUNC_ENTER(voice->audio)
156
157
if (!impl->output_pos)
158
{
159
if (wfx->Format.wFormatTag == FAUDIO_FORMAT_EXTENSIBLE)
160
{
161
const FAudioBufferWMA *wma = &voice->src.bufferList->bufferWMA;
162
const UINT32 *output_sizes = wma->pDecodedPacketCumulativeBytes;
163
164
impl->input_size = wfx->Format.nBlockAlign;
165
impl->output_size = max(
166
impl->output_size,
167
output_sizes[wma->PacketCount - 1]
168
);
169
}
170
else
171
{
172
const FAudioXMA2WaveFormat *xwf = (const FAudioXMA2WaveFormat *)wfx;
173
174
impl->input_size = xwf->dwBytesPerBlock;
175
impl->output_size = max(
176
impl->output_size,
177
(size_t) xwf->dwSamplesEncoded *
178
voice->src.format->nChannels *
179
(voice->src.format->wBitsPerSample / 8)
180
);
181
}
182
183
impl->output_buf = voice->audio->pRealloc(
184
impl->output_buf,
185
impl->output_size
186
);
187
FAudio_assert(impl->output_buf && "Failed to allocate output buffer!");
188
189
LOG_INFO(voice->audio, "sending BOS to %p", impl->decoder);
190
hr = IMFTransform_ProcessMessage(
191
impl->decoder,
192
MFT_MESSAGE_NOTIFY_START_OF_STREAM,
193
0
194
);
195
FAudio_assert(!FAILED(hr) && "Failed to notify decoder stream start!");
196
FAudio_WMAMF_ProcessInput(voice, buffer);
197
}
198
199
samples_pos = voice->src.curBufferOffset * voice->src.format->nChannels * sizeof(float);
200
samples_size = samples * voice->src.format->nChannels * sizeof(float);
201
202
while (impl->output_pos < samples_pos + samples_size)
203
{
204
hr = FAudio_WMAMF_ProcessOutput(voice, buffer);
205
if (FAILED(hr)) goto error;
206
if (hr == S_OK) continue;
207
208
hr = FAudio_WMAMF_ProcessInput(voice, buffer);
209
if (FAILED(hr)) goto error;
210
if (hr == S_OK) continue;
211
212
if (!impl->input_size) break;
213
214
LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);
215
hr = IMFTransform_ProcessMessage(
216
impl->decoder,
217
MFT_MESSAGE_NOTIFY_END_OF_STREAM,
218
0
219
);
220
FAudio_assert(!FAILED(hr) && "Failed to send EOS!");
221
impl->input_size = 0;
222
}
223
224
if (impl->output_pos > samples_pos)
225
{
226
copy_size = FAudio_min(impl->output_pos - samples_pos, samples_size);
227
FAudio_memcpy(decodeCache, impl->output_buf + samples_pos, copy_size);
228
}
229
FAudio_zero((char *)decodeCache + copy_size, samples_size - copy_size);
230
LOG_INFO(
231
voice->audio,
232
"decoded %Ix / %Ix bytes, copied %Ix / %Ix bytes",
233
impl->output_pos,
234
impl->output_size,
235
copy_size,
236
samples_size
237
);
238
239
LOG_FUNC_EXIT(voice->audio)
240
return;
241
242
error:
243
FAudio_zero(decodeCache, samples * voice->src.format->nChannels * sizeof(float));
244
LOG_FUNC_EXIT(voice->audio)
245
}
246
247
uint32_t FAudio_WMADEC_init(FAudioSourceVoice *voice, uint32_t type)
248
{
249
static const uint8_t fake_codec_data[16] = {0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
250
uint8_t fake_codec_data_wma3[18] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 0, 0, 0};
251
const FAudioWaveFormatExtensible *wfx = (FAudioWaveFormatExtensible *)voice->src.format;
252
struct FAudioWMADEC *impl;
253
MFT_OUTPUT_STREAM_INFO info = {0};
254
IMFMediaBuffer *media_buffer;
255
IMFMediaType *media_type;
256
IMFTransform *decoder;
257
HRESULT hr;
258
UINT32 i, value;
259
GUID guid;
260
261
LOG_FUNC_ENTER(voice->audio)
262
263
if (!(impl = voice->audio->pMalloc(sizeof(*impl)))) return -1;
264
FAudio_memset(impl, 0, sizeof(*impl));
265
266
hr = CoCreateInstance(
267
&CLSID_CWMADecMediaObject,
268
0,
269
CLSCTX_INPROC_SERVER,
270
&IID_IMFTransform,
271
(void **)&decoder
272
);
273
if (FAILED(hr))
274
{
275
voice->audio->pFree(impl->output_buf);
276
return -2;
277
}
278
279
hr = MFCreateMediaType(&media_type);
280
FAudio_assert(!FAILED(hr) && "Failed create media type!");
281
hr = IMFMediaType_SetGUID(
282
media_type,
283
&MF_MT_MAJOR_TYPE,
284
&MFMediaType_Audio
285
);
286
FAudio_assert(!FAILED(hr) && "Failed set media major type!");
287
288
switch (type)
289
{
290
case FAUDIO_FORMAT_WMAUDIO2:
291
hr = IMFMediaType_SetBlob(
292
media_type,
293
&MF_MT_USER_DATA,
294
(void *)fake_codec_data,
295
sizeof(fake_codec_data)
296
);
297
FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
298
hr = IMFMediaType_SetGUID(
299
media_type,
300
&MF_MT_SUBTYPE,
301
&MFAudioFormat_WMAudioV8
302
);
303
FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
304
hr = IMFMediaType_SetUINT32(
305
media_type,
306
&MF_MT_AUDIO_BLOCK_ALIGNMENT,
307
wfx->Format.nBlockAlign
308
);
309
FAudio_assert(!FAILED(hr) && "Failed set input block align!");
310
break;
311
case FAUDIO_FORMAT_WMAUDIO3:
312
*(uint16_t *)fake_codec_data_wma3 = voice->src.format->wBitsPerSample;
313
for (i = 0; i < voice->src.format->nChannels; i++)
314
{
315
fake_codec_data_wma3[2] <<= 1;
316
fake_codec_data_wma3[2] |= 1;
317
}
318
hr = IMFMediaType_SetBlob(
319
media_type,
320
&MF_MT_USER_DATA,
321
(void *)fake_codec_data_wma3,
322
sizeof(fake_codec_data_wma3)
323
);
324
FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
325
hr = IMFMediaType_SetGUID(
326
media_type,
327
&MF_MT_SUBTYPE,
328
&MFAudioFormat_WMAudioV9
329
);
330
FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
331
hr = IMFMediaType_SetUINT32(
332
media_type,
333
&MF_MT_AUDIO_BLOCK_ALIGNMENT,
334
wfx->Format.nBlockAlign
335
);
336
FAudio_assert(!FAILED(hr) && "Failed set input block align!");
337
break;
338
case FAUDIO_FORMAT_WMAUDIO_LOSSLESS:
339
hr = IMFMediaType_SetBlob(
340
media_type,
341
&MF_MT_USER_DATA,
342
(void *)&wfx->Samples,
343
wfx->Format.cbSize
344
);
345
FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
346
hr = IMFMediaType_SetGUID(
347
media_type,
348
&MF_MT_SUBTYPE,
349
&MFAudioFormat_WMAudio_Lossless
350
);
351
FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
352
hr = IMFMediaType_SetUINT32(
353
media_type,
354
&MF_MT_AUDIO_BLOCK_ALIGNMENT,
355
wfx->Format.nBlockAlign
356
);
357
FAudio_assert(!FAILED(hr) && "Failed set input block align!");
358
break;
359
case FAUDIO_FORMAT_XMAUDIO2:
360
{
361
const FAudioXMA2WaveFormat *xwf = (const FAudioXMA2WaveFormat *)wfx;
362
hr = IMFMediaType_SetBlob(
363
media_type,
364
&MF_MT_USER_DATA,
365
(void *)&wfx->Samples,
366
wfx->Format.cbSize
367
);
368
FAudio_assert(!FAILED(hr) && "Failed set codec private data!");
369
hr = IMFMediaType_SetGUID(
370
media_type,
371
&MF_MT_SUBTYPE,
372
&MFAudioFormat_XMAudio2
373
);
374
FAudio_assert(!FAILED(hr) && "Failed set media sub type!");
375
hr = IMFMediaType_SetUINT32(
376
media_type,
377
&MF_MT_AUDIO_BLOCK_ALIGNMENT,
378
xwf->dwBytesPerBlock
379
);
380
FAudio_assert(!FAILED(hr) && "Failed set input block align!");
381
break;
382
}
383
default:
384
FAudio_assert(0 && "Unsupported type!");
385
break;
386
}
387
388
hr = IMFMediaType_SetUINT32(
389
media_type,
390
&MF_MT_AUDIO_BITS_PER_SAMPLE,
391
wfx->Format.wBitsPerSample
392
);
393
FAudio_assert(!FAILED(hr) && "Failed set input bits per sample!");
394
hr = IMFMediaType_SetUINT32(
395
media_type,
396
&MF_MT_AUDIO_AVG_BYTES_PER_SECOND,
397
wfx->Format.nAvgBytesPerSec
398
);
399
FAudio_assert(!FAILED(hr) && "Failed set input bytes per sample!");
400
hr = IMFMediaType_SetUINT32(
401
media_type,
402
&MF_MT_AUDIO_NUM_CHANNELS,
403
wfx->Format.nChannels
404
);
405
FAudio_assert(!FAILED(hr) && "Failed set input channel count!");
406
hr = IMFMediaType_SetUINT32(
407
media_type,
408
&MF_MT_AUDIO_SAMPLES_PER_SECOND,
409
wfx->Format.nSamplesPerSec
410
);
411
FAudio_assert(!FAILED(hr) && "Failed set input sample rate!");
412
413
hr = IMFTransform_SetInputType(
414
decoder,
415
0,
416
media_type,
417
0
418
);
419
FAudio_assert(!FAILED(hr) && "Failed set decoder input type!");
420
IMFMediaType_Release(media_type);
421
422
i = 0;
423
while (SUCCEEDED(hr))
424
{
425
hr = IMFTransform_GetOutputAvailableType(
426
decoder,
427
0,
428
i++,
429
&media_type
430
);
431
FAudio_assert(!FAILED(hr) && "Failed get output media type!");
432
433
hr = IMFMediaType_GetGUID(
434
media_type,
435
&MF_MT_MAJOR_TYPE,
436
&guid
437
);
438
FAudio_assert(!FAILED(hr) && "Failed get media major type!");
439
if (!IsEqualGUID(&MFMediaType_Audio, &guid)) goto next;
440
441
hr = IMFMediaType_GetGUID(
442
media_type,
443
&MF_MT_SUBTYPE,
444
&guid
445
);
446
FAudio_assert(!FAILED(hr) && "Failed get media major type!");
447
if (!IsEqualGUID(&MFAudioFormat_Float, &guid)) goto next;
448
449
hr = IMFMediaType_GetUINT32(
450
media_type,
451
&MF_MT_AUDIO_BITS_PER_SAMPLE,
452
&value
453
);
454
if (FAILED(hr))
455
{
456
value = 32;
457
hr = IMFMediaType_SetUINT32(
458
media_type,
459
&MF_MT_AUDIO_BITS_PER_SAMPLE,
460
value
461
);
462
}
463
FAudio_assert(!FAILED(hr) && "Failed get bits per sample!");
464
if (value != 32) goto next;
465
466
hr = IMFMediaType_GetUINT32(
467
media_type,
468
&MF_MT_AUDIO_NUM_CHANNELS,
469
&value
470
);
471
if (FAILED(hr))
472
{
473
value = wfx->Format.nChannels;
474
hr = IMFMediaType_SetUINT32(
475
media_type,
476
&MF_MT_AUDIO_NUM_CHANNELS,
477
value
478
);
479
}
480
FAudio_assert(!FAILED(hr) && "Failed get channel count!");
481
if (value != wfx->Format.nChannels) goto next;
482
483
hr = IMFMediaType_GetUINT32(
484
media_type,
485
&MF_MT_AUDIO_SAMPLES_PER_SECOND,
486
&value
487
);
488
if (FAILED(hr))
489
{
490
value = wfx->Format.nSamplesPerSec;
491
hr = IMFMediaType_SetUINT32(
492
media_type,
493
&MF_MT_AUDIO_SAMPLES_PER_SECOND,
494
value
495
);
496
}
497
FAudio_assert(!FAILED(hr) && "Failed get sample rate!");
498
if (value != wfx->Format.nSamplesPerSec) goto next;
499
500
hr = IMFMediaType_GetUINT32(
501
media_type,
502
&MF_MT_AUDIO_BLOCK_ALIGNMENT,
503
&value
504
);
505
if (FAILED(hr))
506
{
507
value = wfx->Format.nChannels * sizeof(float);
508
hr = IMFMediaType_SetUINT32(
509
media_type,
510
&MF_MT_AUDIO_BLOCK_ALIGNMENT,
511
value
512
);
513
}
514
FAudio_assert(!FAILED(hr) && "Failed get block align!");
515
if (value == wfx->Format.nChannels * sizeof(float)) break;
516
517
next:
518
IMFMediaType_Release(media_type);
519
}
520
FAudio_assert(!FAILED(hr) && "Failed to find output media type!");
521
hr = IMFTransform_SetOutputType(decoder, 0, media_type, 0);
522
FAudio_assert(!FAILED(hr) && "Failed set decoder output type!");
523
IMFMediaType_Release(media_type);
524
525
hr = IMFTransform_GetOutputStreamInfo(decoder, 0, &info);
526
FAudio_assert(!FAILED(hr) && "Failed to get output stream info!");
527
528
impl->decoder = decoder;
529
if (!(info.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES))
530
{
531
hr = MFCreateSample(&impl->output_sample);
532
FAudio_assert(!FAILED(hr) && "Failed to create sample!");
533
hr = MFCreateMemoryBuffer(info.cbSize, &media_buffer);
534
FAudio_assert(!FAILED(hr) && "Failed to create buffer!");
535
hr = IMFSample_AddBuffer(impl->output_sample, media_buffer);
536
FAudio_assert(!FAILED(hr) && "Failed to buffer to sample!");
537
IMFMediaBuffer_Release(media_buffer);
538
}
539
540
hr = IMFTransform_ProcessMessage(
541
decoder,
542
MFT_MESSAGE_NOTIFY_BEGIN_STREAMING,
543
0
544
);
545
FAudio_assert(!FAILED(hr) && "Failed to start decoder stream!");
546
547
voice->src.wmadec = impl;
548
voice->src.decode = FAudio_INTERNAL_DecodeWMAMF;
549
550
LOG_FUNC_EXIT(voice->audio);
551
return 0;
552
}
553
554
void FAudio_WMADEC_free(FAudioSourceVoice *voice)
555
{
556
struct FAudioWMADEC *impl = voice->src.wmadec;
557
HRESULT hr;
558
559
LOG_FUNC_ENTER(voice->audio)
560
FAudio_PlatformLockMutex(voice->audio->sourceLock);
561
LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
562
563
if (impl->input_size)
564
{
565
LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);
566
hr = IMFTransform_ProcessMessage(
567
impl->decoder,
568
MFT_MESSAGE_NOTIFY_END_OF_STREAM,
569
0
570
);
571
FAudio_assert(!FAILED(hr) && "Failed to send EOS!");
572
impl->input_size = 0;
573
}
574
if (impl->output_pos)
575
{
576
LOG_INFO(voice->audio, "sending DRAIN to %p", impl->decoder);
577
hr = IMFTransform_ProcessMessage(
578
impl->decoder,
579
MFT_MESSAGE_COMMAND_DRAIN,
580
0
581
);
582
FAudio_assert(!FAILED(hr) && "Failed to send DRAIN!");
583
impl->output_pos = 0;
584
}
585
586
if (impl->output_sample) IMFSample_Release(impl->output_sample);
587
IMFTransform_Release(impl->decoder);
588
voice->audio->pFree(impl->output_buf);
589
voice->audio->pFree(voice->src.wmadec);
590
voice->src.wmadec = NULL;
591
voice->src.decode = NULL;
592
593
FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
594
LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
595
LOG_FUNC_EXIT(voice->audio)
596
}
597
598
void FAudio_WMADEC_end_buffer(FAudioSourceVoice *voice)
599
{
600
struct FAudioWMADEC *impl = voice->src.wmadec;
601
HRESULT hr;
602
603
LOG_FUNC_ENTER(voice->audio)
604
605
if (impl->input_size)
606
{
607
LOG_INFO(voice->audio, "sending EOS to %p", impl->decoder);
608
hr = IMFTransform_ProcessMessage(
609
impl->decoder,
610
MFT_MESSAGE_NOTIFY_END_OF_STREAM,
611
0
612
);
613
FAudio_assert(!FAILED(hr) && "Failed to send EOS!");
614
impl->input_size = 0;
615
}
616
impl->output_pos = 0;
617
impl->input_pos = 0;
618
619
LOG_FUNC_EXIT(voice->audio)
620
}
621
622
#endif /* HAVE_WMADEC */
623
624