You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

262 lines
7.1 KiB

/*****************************************************************************
* mmdevice.h : Windows Multimedia Device API audio output plugin for VLC
*****************************************************************************
* Copyright (C) 2012 Rémi Denis-Courmont
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef VLC_AOUT_MMDEVICE_H
# define VLC_AOUT_MMDEVICE_H 1
#define MM_PASSTHROUGH_DISABLED 0
#define MM_PASSTHROUGH_ENABLED 1
#define MM_PASSTHROUGH_ENABLED_HD 2
#define MM_PASSTHROUGH_DEFAULT MM_PASSTHROUGH_DISABLED
typedef struct aout_stream aout_stream_t;
/**
* Audio output simplified API for Windows
*/
struct aout_stream
{
struct vlc_object_t obj;
void *sys;
void (*stop)(aout_stream_t *);
HRESULT (*play)(aout_stream_t *, block_t *, vlc_tick_t);
HRESULT (*pause)(aout_stream_t *, bool);
HRESULT (*flush)(aout_stream_t *);
};
struct aout_stream_owner
{
aout_stream_t s;
void *device;
HRESULT (*activate)(void *device, REFIID, PROPVARIANT *, void **);
HANDLE buffer_ready_event;
struct {
vlc_tick_t deadline;
void (*callback)(aout_stream_t *);
} timer;
block_t *chain;
block_t **last;
audio_output_t *aout;
};
/*
* "aout output" helpers
*/
static inline
struct aout_stream_owner *aout_stream_owner(aout_stream_t *s)
{
return container_of(s, struct aout_stream_owner, s);
}
/**
* Creates an audio output stream on a given Windows multimedia device.
* \param s audio output stream object to be initialized
* \param fmt audio output sample format [IN/OUT]
* \param sid audio output session GUID [IN]
*/
typedef HRESULT (*aout_stream_start_t)(aout_stream_t *s,
audio_sample_format_t *fmt, const GUID *sid);
/**
* Destroys an audio output stream.
*/
static inline
void aout_stream_owner_Stop(struct aout_stream_owner *owner)
{
owner->s.stop(&owner->s);
}
static inline
HRESULT aout_stream_owner_Play(struct aout_stream_owner *owner,
block_t *block, vlc_tick_t date)
{
return owner->s.play(&owner->s, block, date);
}
static inline
HRESULT aout_stream_owner_Pause(struct aout_stream_owner *owner, bool paused)
{
return owner->s.pause(&owner->s, paused);
}
static inline
HRESULT aout_stream_owner_Flush(struct aout_stream_owner *owner)
{
block_ChainRelease(owner->chain);
owner->chain = NULL;
owner->last = &owner->chain;
return owner->s.flush(&owner->s);
}
static inline
void aout_stream_owner_AppendBlock(struct aout_stream_owner *owner,
block_t *block, vlc_tick_t date)
{
block->i_dts = date;
block_ChainLastAppend(&owner->last, block);
}
static inline
HRESULT aout_stream_owner_PlayAll(struct aout_stream_owner *owner)
{
HRESULT hr;
block_t *block = owner->chain, *next;
while (block != NULL)
{
next = block->p_next;
vlc_tick_t date = block->i_dts;
block->i_dts = VLC_TICK_INVALID;
hr = aout_stream_owner_Play(owner, block, date);
if (hr == S_FALSE)
return hr;
else
{
block = owner->chain = next;
if (FAILED(hr))
{
if (block == NULL)
owner->last = &owner->chain;
return hr;
}
}
}
owner->last = &owner->chain;
return S_OK;
}
static inline
DWORD aout_stream_owner_ProcessTimer(struct aout_stream_owner *owner)
{
if (owner->timer.deadline != VLC_TICK_INVALID)
{
vlc_tick_t now = vlc_tick_now();
/* Subtract 1 ms since WaitForMultipleObjects will likely sleep
* a little less than requested */
if (now >= owner->timer.deadline - VLC_TICK_FROM_MS(1))
{
assert(owner->timer.callback != NULL);
owner->timer.deadline = VLC_TICK_INVALID;
owner->timer.callback(&owner->s);
/* timer.deadline might be updated from timer.callback */
if (owner->timer.deadline != VLC_TICK_INVALID)
{
now = vlc_tick_now();
return MS_FROM_VLC_TICK(owner->timer.deadline - now);
}
}
else
return MS_FROM_VLC_TICK(owner->timer.deadline - now);
}
return INFINITE;
}
static inline
void *aout_stream_owner_New(audio_output_t *aout, size_t size,
HRESULT (*activate)(void *device, REFIID, PROPVARIANT *, void **))
{
assert(size >= sizeof(struct aout_stream_owner));
void *obj = vlc_object_create(aout, size);
if (unlikely(obj == NULL))
return NULL;
struct aout_stream_owner *owner = obj;
owner->aout = aout;
owner->chain = NULL;
owner->last = &owner->chain;
owner->activate = activate;
owner->timer.deadline = VLC_TICK_INVALID;
owner->timer.callback = NULL;
owner->buffer_ready_event = CreateEvent(NULL, FALSE, FALSE, NULL);
if (unlikely(owner->buffer_ready_event == NULL))
{
vlc_object_delete(&owner->s);
return NULL;
}
return obj;
}
static inline
void aout_stream_owner_Delete(struct aout_stream_owner *owner)
{
CloseHandle(owner->buffer_ready_event);
vlc_object_delete(&owner->s);
}
/*
* "aout stream" helpers
*/
static inline
void aout_stream_TimingReport(aout_stream_t *s, vlc_tick_t system_ts,
vlc_tick_t audio_ts)
{
struct aout_stream_owner *owner = aout_stream_owner(s);
aout_TimingReport(owner->aout, system_ts, audio_ts);
}
static inline
HRESULT aout_stream_Activate(aout_stream_t *s, REFIID iid,
PROPVARIANT *actparms, void **pv)
{
struct aout_stream_owner *owner = aout_stream_owner(s);
return owner->activate(owner->device, iid, actparms, pv);
}
static inline
HANDLE aout_stream_GetBufferReadyEvent(aout_stream_t *s)
{
struct aout_stream_owner *owner = aout_stream_owner(s);
return owner->buffer_ready_event;
}
static inline
void aout_stream_TriggerTimer(aout_stream_t *s,
void (*callback)(aout_stream_t *),
vlc_tick_t deadline)
{
struct aout_stream_owner *owner = aout_stream_owner(s);
owner->timer.deadline = deadline;
owner->timer.callback = callback;
}
static inline
void aout_stream_DisarmTimer(aout_stream_t *s)
{
struct aout_stream_owner *owner = aout_stream_owner(s);
owner->timer.deadline = VLC_TICK_INVALID;
owner->timer.callback = NULL;
}
#endif