mirror of
https://github.com/morgan9e/FreeRDP
synced 2026-04-15 00:44:19 +09:00
Fixed windows audio recording issues:
1. Fix recording channels to mono 2. Fix alignment of PCM data to 2 bytes
This commit is contained in:
@@ -110,27 +110,74 @@ static void CALLBACK waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance
|
||||
setChannelError(winmm->rdpcontext, error, "waveInProc reported an error");
|
||||
}
|
||||
|
||||
static BOOL log_mmresult(AudinWinmmDevice* winmm, const char* what, MMRESULT result)
|
||||
{
|
||||
if (result != MMSYSERR_NOERROR)
|
||||
{
|
||||
CHAR buffer[8192] = { 0 };
|
||||
CHAR msg[8192] = { 0 };
|
||||
CHAR cmsg[8192] = { 0 };
|
||||
waveInGetErrorTextA(result, buffer, sizeof(buffer));
|
||||
|
||||
_snprintf(msg, sizeof(msg) - 1, "%s failed. %" PRIu32 " [%s]", what, result, buffer);
|
||||
_snprintf(cmsg, sizeof(cmsg) - 1, "audin_winmm_thread_func reported an error '%s'", msg);
|
||||
WLog_Print(winmm->log, WLOG_DEBUG, "%s", msg);
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR, cmsg);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL test_format_supported(const PWAVEFORMATEX pwfx)
|
||||
{
|
||||
MMRESULT rc;
|
||||
WAVEINCAPSA caps = { 0 };
|
||||
|
||||
rc = waveInGetDevCapsA(WAVE_MAPPER, &caps, sizeof(caps));
|
||||
if (rc != MMSYSERR_NOERROR)
|
||||
return FALSE;
|
||||
|
||||
switch (pwfx->nChannels)
|
||||
{
|
||||
case 1:
|
||||
if ((caps.dwFormats &
|
||||
(WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08 |
|
||||
WAVE_FORMAT_1M16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_4M16 | WAVE_FORMAT_96M16)) == 0)
|
||||
return FALSE;
|
||||
break;
|
||||
case 2:
|
||||
if ((caps.dwFormats &
|
||||
(WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08 |
|
||||
WAVE_FORMAT_1S16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_96S16)) == 0)
|
||||
return FALSE;
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rc = waveInOpen(NULL, WAVE_MAPPER, pwfx, 0, 0,
|
||||
WAVE_FORMAT_QUERY | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE);
|
||||
return (rc == MMSYSERR_NOERROR);
|
||||
}
|
||||
|
||||
static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
||||
{
|
||||
AudinWinmmDevice* winmm = (AudinWinmmDevice*)arg;
|
||||
char* buffer;
|
||||
int size, i;
|
||||
WAVEHDR waveHdr[4];
|
||||
WAVEHDR waveHdr[4] = { 0 };
|
||||
DWORD status;
|
||||
MMRESULT rc;
|
||||
|
||||
if (!winmm->hWaveIn)
|
||||
{
|
||||
if (MMSYSERR_NOERROR != waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur,
|
||||
(DWORD_PTR)waveInProc, (DWORD_PTR)winmm,
|
||||
CALLBACK_FUNCTION))
|
||||
{
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
|
||||
MMRESULT rc;
|
||||
rc = waveInOpen(&winmm->hWaveIn, WAVE_MAPPER, winmm->pwfx_cur, (DWORD_PTR)waveInProc,
|
||||
(DWORD_PTR)winmm,
|
||||
CALLBACK_FUNCTION | WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE);
|
||||
if (!log_mmresult(winmm, "waveInOpen", rc))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
size =
|
||||
@@ -150,36 +197,22 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
||||
waveHdr[i].lpData = buffer;
|
||||
rc = waveInPrepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
if (!log_mmresult(winmm, "waveInPrepareHeader", rc))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInPrepareHeader failed. %" PRIu32 "", rc);
|
||||
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
rc = waveInAddBuffer(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
if (!log_mmresult(winmm, "waveInAddBuffer", rc))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInAddBuffer failed. %" PRIu32 "", rc);
|
||||
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
}
|
||||
|
||||
rc = waveInStart(winmm->hWaveIn);
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
if (!log_mmresult(winmm, "waveInStart", rc))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInStart failed. %" PRIu32 "", rc);
|
||||
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
status = WaitForSingleObject(winmm->stopEvent, INFINITE);
|
||||
@@ -195,26 +228,17 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
||||
|
||||
rc = waveInReset(winmm->hWaveIn);
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
if (!log_mmresult(winmm, "waveInReset", rc))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInReset failed. %" PRIu32 "", rc);
|
||||
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
rc = waveInUnprepareHeader(winmm->hWaveIn, &waveHdr[i], sizeof(waveHdr[i]));
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
if (!log_mmresult(winmm, "waveInUnprepareHeader", rc))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInUnprepareHeader failed. %" PRIu32 "", rc);
|
||||
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
free(waveHdr[i].lpData);
|
||||
@@ -222,13 +246,8 @@ static DWORD WINAPI audin_winmm_thread_func(LPVOID arg)
|
||||
|
||||
rc = waveInClose(winmm->hWaveIn);
|
||||
|
||||
if (MMSYSERR_NOERROR != rc)
|
||||
if (!log_mmresult(winmm, "waveInClose", rc))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_DEBUG, "waveInClose failed. %" PRIu32 "", rc);
|
||||
|
||||
if (winmm->rdpcontext)
|
||||
setChannelError(winmm->rdpcontext, ERROR_INTERNAL_ERROR,
|
||||
"audin_winmm_thread_func reported an error");
|
||||
}
|
||||
|
||||
winmm->hWaveIn = NULL;
|
||||
@@ -311,16 +330,30 @@ static UINT audin_winmm_set_format(IAudinDevice* device, const AUDIO_FORMAT* for
|
||||
|
||||
for (i = 0; i < winmm->cFormats; i++)
|
||||
{
|
||||
if (winmm->ppwfx[i]->wFormatTag == format->wFormatTag &&
|
||||
winmm->ppwfx[i]->nChannels == format->nChannels &&
|
||||
winmm->ppwfx[i]->wBitsPerSample == format->wBitsPerSample)
|
||||
const PWAVEFORMATEX ppwfx = winmm->ppwfx[i];
|
||||
if ((ppwfx->wFormatTag == format->wFormatTag) && (ppwfx->nChannels == format->nChannels) &&
|
||||
(ppwfx->wBitsPerSample == format->wBitsPerSample) &&
|
||||
(ppwfx->nSamplesPerSec == format->nSamplesPerSec))
|
||||
{
|
||||
winmm->pwfx_cur = winmm->ppwfx[i];
|
||||
break;
|
||||
/* BUG: Many devices report to support stereo recording but fail here.
|
||||
* Ensure we always use mono. */
|
||||
if (ppwfx->nChannels > 1)
|
||||
{
|
||||
ppwfx->nChannels = 1;
|
||||
}
|
||||
|
||||
if (ppwfx->nBlockAlign != 2)
|
||||
{
|
||||
ppwfx->nBlockAlign = 2;
|
||||
}
|
||||
if (!test_format_supported(ppwfx))
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
winmm->pwfx_cur = ppwfx;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
|
||||
@@ -332,6 +365,9 @@ static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMA
|
||||
if (!winmm || !format)
|
||||
return FALSE;
|
||||
|
||||
if (format->wFormatTag != WAVE_FORMAT_PCM)
|
||||
return FALSE;
|
||||
|
||||
pwfx = (PWAVEFORMATEX)malloc(sizeof(WAVEFORMATEX) + format->cbSize);
|
||||
|
||||
if (!pwfx)
|
||||
@@ -346,29 +382,27 @@ static BOOL audin_winmm_format_supported(IAudinDevice* device, const AUDIO_FORMA
|
||||
data = (BYTE*)pwfx + sizeof(WAVEFORMATEX);
|
||||
memcpy(data, format->data, format->cbSize);
|
||||
|
||||
if (pwfx->wFormatTag == WAVE_FORMAT_PCM)
|
||||
pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
|
||||
|
||||
if (!test_format_supported(pwfx))
|
||||
goto fail;
|
||||
|
||||
if (winmm->cFormats >= winmm->ppwfx_size)
|
||||
{
|
||||
pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
|
||||
PWAVEFORMATEX* tmp_ppwfx;
|
||||
tmp_ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
|
||||
|
||||
if (MMSYSERR_NOERROR == waveInOpen(NULL, WAVE_MAPPER, pwfx, 0, 0, WAVE_FORMAT_QUERY))
|
||||
{
|
||||
if (winmm->cFormats >= winmm->ppwfx_size)
|
||||
{
|
||||
PWAVEFORMATEX* tmp_ppwfx;
|
||||
tmp_ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
|
||||
if (!tmp_ppwfx)
|
||||
goto fail;
|
||||
|
||||
if (!tmp_ppwfx)
|
||||
return FALSE;
|
||||
|
||||
winmm->ppwfx_size *= 2;
|
||||
winmm->ppwfx = tmp_ppwfx;
|
||||
}
|
||||
|
||||
winmm->ppwfx[winmm->cFormats++] = pwfx;
|
||||
return TRUE;
|
||||
}
|
||||
winmm->ppwfx_size *= 2;
|
||||
winmm->ppwfx = tmp_ppwfx;
|
||||
}
|
||||
|
||||
winmm->ppwfx[winmm->cFormats++] = pwfx;
|
||||
return TRUE;
|
||||
|
||||
fail:
|
||||
free(pwfx);
|
||||
return FALSE;
|
||||
}
|
||||
@@ -507,7 +541,7 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
}
|
||||
|
||||
winmm->ppwfx_size = 10;
|
||||
winmm->ppwfx = malloc(sizeof(PWAVEFORMATEX) * winmm->ppwfx_size);
|
||||
winmm->ppwfx = calloc(winmm->ppwfx_size, sizeof(PWAVEFORMATEX));
|
||||
|
||||
if (!winmm->ppwfx)
|
||||
{
|
||||
@@ -516,7 +550,7 @@ UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEn
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)winmm)))
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, &winmm->iface)))
|
||||
{
|
||||
WLog_Print(winmm->log, WLOG_ERROR, "RegisterAudinDevice failed with error %" PRIu32 "!",
|
||||
error);
|
||||
|
||||
Reference in New Issue
Block a user