Commit e73d940f authored by Jean-Paul Saman's avatar Jean-Paul Saman

audio_output: Modules.am: add amem audio memory output module

Build audio memory output module.
parent f88ff9a9
...@@ -9,8 +9,10 @@ SOURCES_auhal = auhal.c ...@@ -9,8 +9,10 @@ SOURCES_auhal = auhal.c
SOURCES_jack = jack.c SOURCES_jack = jack.c
SOURCES_pulse = pulse.c SOURCES_pulse = pulse.c
SOURCES_audioqueue = audioqueue.c SOURCES_audioqueue = audioqueue.c
SOURCES_amem = amem.c
libvlc_LTLIBRARIES += libaout_file_plugin.la libvlc_LTLIBRARIES += libaout_file_plugin.la \
libamem_plugin.la
libalsa_plugin_la_SOURCES = alsa.c libalsa_plugin_la_SOURCES = alsa.c
libalsa_plugin_la_CFLAGS = $(AM_CFLAGS) $(ALSA_CFLAGS) libalsa_plugin_la_CFLAGS = $(AM_CFLAGS) $(ALSA_CFLAGS)
......
/*****************************************************************************
* amem.c: memory audio driver for vlc
*****************************************************************************
* Copyright (C) 2010 M2X BV
* $Id$
*
* Authors: Jean-Paul Saman <jpsaman@videolan.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU 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.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_codecs.h>
#include <vlc_es.h>
#include <vlc_aout.h>
#if defined(_WIN32_WINNT) || defined(WIN32)
# include <windows.h>
# include <conio.h>
# include <tchar.h>
//# include "windows_audio_common.h"
#else
# include <limits.h>
# include <errno.h>
# include <sys/types.h>
# include <sys/mman.h>
# include <sys/stat.h> /* For mode constants */
# include <fcntl.h> /* For O_* constants */
# include <semaphore.h>
#endif
#include <assert.h>
/*****************************************************************************
* Local prototypes
*****************************************************************************/
#define FRAME_SIZE 2048
/* Audio channels */
#define CHANNELS_MAX 6
static const int pi_channels_maps[CHANNELS_MAX+1] =
{
0,
AOUT_CHAN_CENTER,
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
AOUT_CHAN_CENTER | AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT,
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
| AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT,
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
| AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE
};
/* Audio format */
static const char *const format_list[] = { "u8", "s8", "u16", "s16", "u16_le",
"s16_le", "u16_be", "s16_be", "fixed32",
"float32", "spdif" };
static const int format_int[] = { VLC_CODEC_U8,
VLC_CODEC_S8,
VLC_CODEC_U16N, VLC_CODEC_S16N,
VLC_CODEC_U16L,
VLC_CODEC_S16L,
VLC_CODEC_U16B,
VLC_CODEC_S16B,
VLC_CODEC_FI32,
VLC_CODEC_FL32,
VLC_CODEC_SPDIFL };
/* Shared memory header structure */
typedef struct
{
#if defined(_WIN32_WINNT) || defined(WIN32)
WAVEFORMATEX wave;
#else
vlc_sem_t sem;
vlc_fourcc_t i_format;
unsigned int i_rate;
unsigned int i_bytes_per_frame;
unsigned int i_frame_length;
unsigned i_bitspersample;
unsigned i_blockalign;
uint8_t i_channels;
#endif
unsigned int i_read_samples;
unsigned int i_write_samples;
unsigned int i_max_samples;
unsigned int i_nb_samples;
void *samples;
} amem_shared_t;
/* Private structure */
struct aout_sys_t
{
char *psz_name;
int i_delay;
#if defined(_WIN32_WINNT) || defined(WIN32)
HANDLE handle;
#else
int shm_fd;
#endif
amem_shared_t *header;
};
static int Open (vlc_object_t *);
static void Close(vlc_object_t *);
static void Play (aout_instance_t *);
static int SetupSharedMem(aout_instance_t *);
static void ExitSharedMem(aout_instance_t *);
/*
* Shared Memory helpers
*/
#if defined(_WIN32_WINNT) || defined(WIN32)
/* FIXME: not tested yet */
static int amem_shmem_create(aout_instance_t *aout, ssize_t len)
{
aout_sys_t *sys = (aout_sys_t *)aout->output.p_sys;
TCHAR szName[] = TEXT(sys->psz_name);
sys->handle = CreateFileMapping(
INVALID_HANDLE_VALUE, // use paging file
NULL, // default security
PAGE_READWRITE, // read/write access
0, // maximum object size (high-order DWORD)
len, // maximum object size (low-order DWORD)
szName); // name of mapping object
if (sys->handle == NULL)
{
msg_Err(aout, "could not create file mapping object (%d).",
GetLastError());
return VLC_EGENERIC;
}
/* Map shared buffer */
sys->header = (amem_shared_t *) MapViewOfFile(sys->handle, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
len);
if (sys->header == NULL)
{
msg_Err(aout, "could not map view of file (%d).",
GetLastError());
CloseHandle(sys->handle);
return VLC_ENGERIC;
}
sys->header->samples = sys->header + sizeof(amem_shared_t);
return VLC_SUCCESS;
}
static int amem_shmem_open(aout_instance_t *aout, ssize_t len)
{
aout_sys_t *sys = (aout_sys_t *)aout->output.p_sys;
TCHAR szName[]=TEXT(sys->psz_name);
sys->handle = OpenFileMapping(
FILE_MAP_ALL_ACCESS, // read/write access
FALSE, // do not inherit the name
szName); // name of mapping object
if (sys->handle == NULL)
{
msg_Err(aout, "could not open file mapping object (%d).",
GetLastError());
return VLC_EGENERIC;
}
sys->header = (amem_shared_t *) MapViewOfFile(handle, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
len);
if (sys->header == NULL)
{
msg_Err(aout, "could not map view of file (%d).",
GetLastError());
CloseHandle(sys->handle);
return VLC_EGENERIC;
}
sys->header->samples = sys->header + sizeof(amem_shared_t);
/* FIXME: initialize semaphore */
return VLC_SUCCESS;
}
static void amem_shmem_close(aout_instance_t *aout)
{
aout_sys_t *sys = (aout_sys_t *)aout->output.p_sys;
/* FIXME: release semaphore */
UnmapViewOfFile(sys->header);
CloseHandle(sys->handle);
}
static void amem_shmem_header(aout_instance_t *aout)
{
aout_sys_t *sys = (aout_sys_t *)aout->output.p_sys;
vlc_sem_wait(&sys->header->sem);
/* Fill header struct */
uint16_t tag = 0;
fourcc_to_wf_tag(aout->output.output.i_format, &tag);
sys->header->wave.wFormatTag = tag;
sys->header->wave.nChannels = aout->output.output.i_channels;
sys->header->wave.nSamplesPerSec = aout->output.output.i_rate * aout->output.i_nb_samples;
//sys->header->wave.nAvgBytesPerSec = ;
sys->header->wave.nBlockAlign = aout->output.output.i_blockalign;
sys->header->wave.wBitsPerSample = aout->output.output.i_bitspersample;
sys->header->wave.cbSize = 0; /* No additional format metadata */
sys->header->i_max_samples = aout->output.i_nb_samples;
sys->header->i_nb_samples = 0;
msg_Info(aout, "samples %d, bits per sample %d",
aout->output.i_nb_samples, aout->output.output.i_bitspersample);
/* FIXME: do not assume entire buffer is available */
memset(sys->header->samples, 0, (aout->output.i_nb_samples *
aout->output.output.i_bitspersample));
if (vlc_sem_post(&sys->header->sem) < 0)
msg_Err(aout, "failed unlocking semaphore (%m)");
}
static void amem_shmem_write(aout_instance_t *aout, aout_buffer_t *data)
{
aout_sys_t *sys = (aout_sys_t *)aout->output.p_sys;
/* FIXME: lock Semaphore */
/* FIXME: Do not assume we can fill entire buffer */
CopyMemory((PVOID)sys->header->samples, data->p_buffer, data->i_buffer);
sys->header->i_nb_samples = p_buffer->i_buffer / (sys->header->i_bitspersample / 8);
/* FIXME: unlock Semaphore */
}
#else
static void amem_sem_init(vlc_sem_t *sem, unsigned value)
{
if (unlikely(sem_init(sem, 1 /* share between process*/, value)) == -1)
abort();
}
static int amem_shmem_create(aout_instance_t *aout, ssize_t len)
{
aout_sys_t *sys = (aout_sys_t *)aout->output.p_sys;
sys->shm_fd = shm_open(sys->psz_name, (O_CREAT | O_EXCL | O_RDWR), (S_IREAD | S_IWRITE));
if (sys->shm_fd < 0)
{
msg_Err(aout, "could not open shared memory object (%m)");
return VLC_EGENERIC;
}
/* set correct maximum size */
if (ftruncate(sys->shm_fd, len) < 0)
{
msg_Err(aout, "failed limiting shared memory size (%m)");
return VLC_EGENERIC;
}
/* mmap shared segment */
sys->header = mmap(0, len, (PROT_READ | PROT_WRITE),
MAP_SHARED, sys->shm_fd, 0);
if (sys->header == MAP_FAILED)
{
msg_Err(aout, "could not mmap shared memory object (%m)");
return VLC_EGENERIC;
}
sys->header->samples = sys->header + sizeof(amem_shared_t);
/* initialize unamed semaphore */
amem_sem_init(&sys->header->sem, 1);
msg_Dbg(aout, "shared memory created");
return VLC_SUCCESS;
}
static int amem_shmem_open(aout_instance_t *aout, ssize_t len)
{
aout_sys_t *sys = (aout_sys_t *)aout->output.p_sys;
sys->shm_fd = shm_open(sys->psz_name, O_RDWR, (S_IREAD | S_IWRITE));
if (sys->shm_fd < 0)
{
msg_Err(aout, "could not open shared memory object (%m)");
return VLC_EGENERIC;
}
/* set correct maximum size */
if (ftruncate(sys->shm_fd, len) < 0)
{
msg_Err(aout, "failed limiting shared memory size (%m)");
return VLC_EGENERIC;
}
/* mmap shared segment */
sys->header = mmap(0, len, (PROT_READ | PROT_WRITE),
MAP_SHARED, sys->shm_fd, 0);
if (sys->header == MAP_FAILED)
{
msg_Err(aout, "could not mmap shared memory object (%m)");
return VLC_EGENERIC;
}
sys->header->samples = sys->header + sizeof(amem_shared_t);
msg_Dbg(aout, "shared memory opened");
return VLC_SUCCESS;
}
static void amem_shmem_close(aout_instance_t *aout)
{
aout_sys_t *sys = (aout_sys_t *)aout->output.p_sys;
vlc_sem_destroy(&sys->header->sem);
/* Determine shared memory size */
if (sys->header != MAP_FAILED)
{
ssize_t len = sizeof(amem_shared_t) + aout->output.i_nb_samples
* aout->output.output.i_bitspersample;
if (munmap(sys->header, len) < 0)
{
msg_Err(aout, "failed unmapping shared memory (%m)");
}
}
close(sys->shm_fd);
msg_Dbg(aout, "shared memory closed");
}
static void amem_shmem_header(aout_instance_t *aout)
{
aout_sys_t *sys = (aout_sys_t *)aout->output.p_sys;
amem_shared_t *header = sys->header;
msg_Dbg(aout, "syncing shared header");
vlc_sem_wait(&header->sem);
/* Fill header struct */
header->i_format = aout->output.output.i_format;
header->i_bitspersample = aout->output.output.i_bitspersample;
header->i_bytes_per_frame = aout->output.output.i_bytes_per_frame;
header->i_channels = aout->output.output.i_channels;
header->i_frame_length = aout->output.output.i_frame_length;
header->i_rate = aout->output.output.i_rate;
header->i_blockalign = aout->output.output.i_blockalign;
header->i_read_samples = 0;
header->i_write_samples = 0;
header->i_max_samples = aout->output.i_nb_samples;
header->i_nb_samples = 0;
memset(header->samples, 0, (aout->output.i_nb_samples *
aout->output.output.i_bitspersample));
if (vlc_sem_post(&header->sem) < 0)
msg_Err(aout, "failed unlocking semaphore (%m)");
}
static int amem_shmem_write(aout_instance_t *aout, aout_buffer_t *p_buffer)
{
aout_sys_t *sys = (aout_sys_t *)aout->output.p_sys;
#if 0
int again = 0;
do
{
if (sem_trywait(&sys->header->sem) == 0)
break;
if (errno == EAGAIN)
again++;
if (again == 3)
{
msg_Err(aout, "Failed locking semaphore for writing");
return VLC_EGENERIC;
}
} while (again < 3);
#endif
vlc_sem_wait(&sys->header->sem);
/* FIXME: do not assume we can fill the entire buffer */
assert(p_buffer->i_nb_samples <= sys->header->i_max_samples);
memcpy(sys->header->samples, p_buffer->p_buffer, p_buffer->i_buffer);
sys->header->i_nb_samples = p_buffer->i_nb_samples;
sys->header->i_write_samples += p_buffer->i_nb_samples;
if (vlc_sem_post(&sys->header->sem) < 0)
msg_Err(aout, "failed unlocking semaphore (%m)");
return VLC_SUCCESS;
}
#endif
/*****************************************************************************
* Module descriptor
*****************************************************************************/
#define ARATE_TEXT N_("Audio sample rate")
#define ARATE_LONGTEXT N_( \
"Sample rate of the audio stream (11250, 22500, 44100 or 48000).")
#define ACHANS_TEXT N_("Audio channels")
#define ACHANS_LONGTEXT N_( \
"Number of audio channels in the audio output." )
#define AFORMAT_TEXT N_("Output format")
#define AFORMAT_LONGTEXT N_( \
"One of \"u8\", \"s8\", \"u16\", \"s16\", " \
"\"u16_le\", \"s16_le\", \"u16_be\", \"s16_be\", \"fixed32\", " \
"\"float32\" or \"spdif\"")
#define ADELAY_TEXT N_("Output delay")
#define ADELAY_LONGTEXT N_( \
"Initial delay for audio output" )
#define ANAME_TEXT N_("Name to use (default /amem.shared)")
#define ANAME_LONGTEXT N_( \
"Name to use as identifier to the (named) shared memory" )
#define AMEM_CFG_PREFIX "amem-"
vlc_module_begin()
set_description(N_("Audio memory output"))
set_shortname(N_("Audio memory"))
set_category( CAT_AUDIO )
set_subcategory( SUBCAT_AUDIO_AOUT )
set_capability("audio output", 0)
add_string( AMEM_CFG_PREFIX "name", "/amem.shared", NULL, ANAME_TEXT,
ANAME_LONGTEXT, true )
add_string( AMEM_CFG_PREFIX "format", "s16", NULL, AFORMAT_TEXT,
AFORMAT_LONGTEXT, true )
change_string_list( format_list, 0, 0 )
add_integer( AMEM_CFG_PREFIX "channels", 2, NULL, ACHANS_TEXT,
ACHANS_LONGTEXT, false )
change_integer_range( 0, CHANNELS_MAX )
add_integer( AMEM_CFG_PREFIX "samplerate", 44100, NULL, ARATE_TEXT,
ARATE_LONGTEXT, true )
add_integer( AMEM_CFG_PREFIX "delay", 0, NULL, ADELAY_TEXT,
ADELAY_TEXT, true )
set_callbacks(Open, Close)
vlc_module_end()
/*****************************************************************************
* Open
*****************************************************************************/
static int Open(vlc_object_t *p_this)
{
aout_instance_t *aout = (aout_instance_t*) p_this;
aout_sys_t *sys;
char *psz_format;
const char * const *ppsz_compare = format_list;
int i = 0;
/* */
aout->output.p_sys = sys = (aout_sys_t *) malloc(sizeof(aout_sys_t));
if (sys == NULL)
return VLC_ENOMEM;
/* */
sys->header = NULL;
aout->output.pf_play = Play;
/* Name to use */
sys->psz_name = var_CreateGetString(p_this, AMEM_CFG_PREFIX "name");
/* Audio sample rate */
aout->output.output.i_rate = var_CreateGetInteger(p_this, AMEM_CFG_PREFIX "samplerate");
/* Audio output delay */
sys->i_delay = var_CreateGetInteger(p_this, AMEM_CFG_PREFIX "delay");
/* Audio format */
psz_format = var_CreateGetString(p_this, AMEM_CFG_PREFIX "format");
while (*ppsz_compare != NULL)
{
if (!strncmp(*ppsz_compare, psz_format, strlen(*ppsz_compare)))
{
break;
}
ppsz_compare++; i++;
}
if (*ppsz_compare == NULL)
{
msg_Err(aout, "cannot understand the format string (%s)",
psz_format);
free(aout->output.p_sys);
free(psz_format);
return VLC_EGENERIC;
}
free(psz_format);
aout->output.output.i_format = format_int[i];
if (AOUT_FMT_NON_LINEAR(&aout->output.output))
{
aout->output.i_nb_samples = A52_FRAME_NB;
aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
aout->output.output.i_frame_length = A52_FRAME_NB;
aout_VolumeNoneInit(aout);
}
else
{
aout->output.i_nb_samples = FRAME_SIZE;
aout_VolumeSoftInit(aout);
}
/* Channels number */
int i_channels = var_CreateGetInteger(p_this, AMEM_CFG_PREFIX "channels");
if ((i_channels > 0) && (i_channels <= CHANNELS_MAX))
{
aout->output.output.i_physical_channels =
pi_channels_maps[i_channels];
}
/* Setup shared memory connection */
if (SetupSharedMem(aout) != VLC_SUCCESS)
{
Close(p_this);
return VLC_EGENERIC;
}
return VLC_SUCCESS;
}
/*****************************************************************************
* Close
*****************************************************************************/
static void Close(vlc_object_t *p_this)
{
aout_instance_t *aout = (aout_instance_t*) p_this;
ExitSharedMem(aout);
free(aout->output.p_sys->psz_name);
free(aout->output.p_sys);
}
/*****************************************************************************
* Play
*****************************************************************************/
static void Play(aout_instance_t *aout)
{
aout_buffer_t *p_buffer;
p_buffer = aout_FifoPop(aout, &aout->output.fifo);
if (p_buffer == NULL)
return;
if (amem_shmem_write(aout, p_buffer) != VLC_SUCCESS)
{
msg_Err(aout, "write error (%m)");
}
aout_BufferFree(p_buffer);
}
/*****************************************************************************
* Shared Memory
*****************************************************************************/
static int SetupSharedMem(aout_instance_t *aout)
{
aout_sys_t *sys = (aout_sys_t *)aout->output.p_sys;
/* Determine shared memory size */
ssize_t len = sizeof(amem_shared_t) + aout->output.i_nb_samples
* aout->output.output.i_bitspersample;
if (*sys->psz_name != '/')
{
msg_Err(aout, "invalid shared segment name (does not begin with /)");
return VLC_EGENERIC;
}
if (strlen(sys->psz_name) >= NAME_MAX)
{
msg_Err(aout, "invalid shared segment name (string length >= %d)", NAME_MAX);
return VLC_EGENERIC;
}
if (amem_shmem_open(aout, len) != VLC_SUCCESS)
return VLC_EGENERIC;
/* Fill header struct */
amem_shmem_header(aout);
return VLC_SUCCESS;
}
static void ExitSharedMem(aout_instance_t *aout)
{
amem_shmem_close(aout);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment