root/modules/oss_audio/oss.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. OSS_Setup
  2. OSS_Shutdown
  3. OSS_ConfigureOutput
  4. OSS_WriteAudio
  5. OSS_SetVolume
  6. OSS_SetPan
  7. OSS_SetPriority
  8. OSS_GetAudioDelay
  9. OSS_QueryOutputSampleRate
  10. NewOSSRender
  11. DeleteOSSRender
  12. QueryInterfaces
  13. LoadInterface
  14. ShutdownInterface

/*
 *                      GPAC - Multimedia Framework C SDK
 *
 *                      Authors: Jean Le Feuvre
 *                      Copyright (c) Telecom ParisTech 2000-2012
 *                                      All rights reserved
 *
 *  This file is part of GPAC / linux_oss audio render module
 *
 *  GPAC 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, or (at your option)
 *  any later version.
 *
 *  GPAC 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 library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#if defined(__DARWIN__) || defined(__APPLE__)
#include <soundcard.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>

#else

#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>

#ifdef OSS_FIX_INC
#include <soundcard.h>
#else
#include <sys/soundcard.h>
#endif

#endif

#include <gpac/modules/audio_out.h>

#define OSS_AUDIO_DEVICE        "/dev/dsp"

typedef struct
{
        int audio_dev, sr, nb_ch;
        u32 buf_size, delay, num_buffers, total_duration;
        u32 force_sr;
        char *wav_buf;
} OSSContext;


#define OSSCTX()        OSSContext *ctx = (OSSContext *)dr->opaque;


static GF_Err OSS_Setup(GF_AudioOutput*dr, void *os_handle, u32 num_buffers, u32 total_duration)
{
        int audio;
        const char *opt;
        OSSCTX();

        opt = gf_modules_get_option((GF_BaseInterface *)dr, "OSS", "ForceSampleRate");
        if (opt) ctx->force_sr = atoi(opt);

        /*open OSS in non-blocking mode*/
        audio = open(OSS_AUDIO_DEVICE, 0);
        if (audio < 0) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[OSS] Cannot open audio device\n"));
                return GF_NOT_SUPPORTED;
        }

        /*set blocking mode back*/
        //fcntl(audio, F_SETFL, fcntl(audio, F_GETFL) & ~FNDELAY);
        ctx->audio_dev=audio;
        ctx->num_buffers = num_buffers;
        ctx->total_duration = total_duration;
        return GF_OK;
}

static void OSS_Shutdown(GF_AudioOutput*dr)
{
        OSSCTX();
        ioctl(ctx->audio_dev,SNDCTL_DSP_RESET,NULL);
        close(ctx->audio_dev);
        if (ctx->wav_buf) gf_free(ctx->wav_buf);
        ctx->wav_buf = NULL;
}


static GF_Err OSS_ConfigureOutput(GF_AudioOutput*dr, u32 *SampleRate, u32 *NbChannels, u32 *nbBitsPerSample, u32 channel_cfg)
{
        int format, blockalign, nb_bufs, frag_spec;
        long flags;
        OSSCTX();

        if (!ctx) return GF_BAD_PARAM;
        /* reset and reopen audio-device */
        ioctl(ctx->audio_dev,SNDCTL_DSP_RESET,NULL);
        close(ctx->audio_dev);
        if (ctx->wav_buf) gf_free(ctx->wav_buf);
        ctx->wav_buf = NULL;
        ctx->audio_dev=open(OSS_AUDIO_DEVICE,O_WRONLY|O_NONBLOCK);
        if (!ctx->audio_dev) return GF_IO_ERR;

        /* Make the file descriptor use blocking writes with fcntl() so that
         we don't have to handle sleep() ourselves*/
        flags = fcntl(ctx->audio_dev, F_GETFL);
        flags &= ~O_NONBLOCK;
        if (fcntl(ctx->audio_dev, F_SETFL, flags) < 0 ) return GF_IO_ERR;

        ctx->nb_ch = (int) (*NbChannels);
        if (ioctl(ctx->audio_dev, SNDCTL_DSP_CHANNELS, &ctx->nb_ch)==-1) return GF_IO_ERR;

        blockalign = ctx->nb_ch;
        if ((*nbBitsPerSample) == 16) {
                blockalign *= 2;
                format = AFMT_S16_LE;
        } else {
                format = AFMT_S8;
        }
        if(ioctl(ctx->audio_dev, SNDCTL_DSP_SETFMT,&format)==-1) return GF_IO_ERR;
        ctx->sr = (*SampleRate);
        if(ioctl(ctx->audio_dev, SNDCTL_DSP_SPEED,&ctx->sr)==-1) return GF_IO_ERR;

        nb_bufs = ctx->num_buffers ? ctx->num_buffers : 8;
        ctx->buf_size = (*SampleRate * blockalign * ctx->total_duration) / (1000 * nb_bufs);
        frag_spec = 4;
        while (ctx->buf_size > (1<<(frag_spec+1)))
                frag_spec++;

        ctx->buf_size = 1<<frag_spec;

        ctx->delay = (1000*ctx->buf_size) / (*SampleRate * blockalign);
        frag_spec = ((nb_bufs<<16) & 0xFFFF0000) | frag_spec;

        ctx->delay = (1000*ctx->buf_size*nb_bufs) / (*SampleRate * blockalign);
        if ( ioctl(ctx->audio_dev, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) return GF_IO_ERR;

        GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[OSS] setup %d buffers %d bytes each (%d ms buffer delay)", nb_bufs, ctx->buf_size, ctx->delay));
        ctx->wav_buf = gf_realloc(ctx->wav_buf, ctx->buf_size*sizeof(char));
        if(!ctx->wav_buf) return GF_OUT_OF_MEM;
        memset(ctx->wav_buf, 0, ctx->buf_size*sizeof(char));
        return GF_OK;
}

static void OSS_WriteAudio(GF_AudioOutput*dr)
{
        u32 written;
        OSSCTX();
        written = dr->FillBuffer(dr->audio_renderer, ctx->wav_buf, ctx->buf_size);
        /*this will also perform sleep*/
        if (written) {
                u32 reallyWritten = write(ctx->audio_dev, ctx->wav_buf, written);
                if (reallyWritten != written) {
                        GF_LOG(GF_LOG_INFO, GF_LOG_MMIO, ("[OSS] Failed to write all audio to device, has written %u, should have %u", reallyWritten, written));
                }
        }
}

static void OSS_SetVolume(GF_AudioOutput*dr, u32 Volume) {}
static void OSS_SetPan(GF_AudioOutput*dr, u32 Pan) {}
static void OSS_SetPriority(GF_AudioOutput*dr, u32 Priority) {}
static u32 OSS_GetAudioDelay(GF_AudioOutput*dr)
{
        OSSCTX()
        return ctx->delay;
}

/*
 * to get the best matching samplerate the oss-device can be set up
 * with the desired sr. if not supported the returned value contains the
 * best matching sr.
 *
 * todo: supported samplerate could depend on nb_channels and format
 */
static GF_Err OSS_QueryOutputSampleRate(GF_AudioOutput*dr, u32 *desired_sr, u32 *NbChannels, u32 *nbBitsPerSample)
{
#ifdef FORCE_SR_LIMIT
        *NbChannels = 2;
        if (!( *desired_sr % 11025)) return GF_OK;
        if (*desired_sr<22050) *desired_sr = 22050;
        else *desired_sr = 44100;
        return GF_OK;
#else
        /* reset and reopen audio-device */
        int i;
        OSSCTX();
        if (ctx->force_sr) {
                *desired_sr = ctx->force_sr;
                return GF_OK;
        }
        i=*desired_sr;
        if(ioctl(ctx->audio_dev, SNDCTL_DSP_SPEED,&i)==-1) return GF_IO_ERR;
        GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[OSS] uses samplerate %d for desired sr %d\n", i, *desired_sr));
        *desired_sr = i;
        i = *NbChannels;
        if(ioctl(ctx->audio_dev,SNDCTL_DSP_CHANNELS, &i)==-1) return GF_IO_ERR;
        GF_LOG(GF_LOG_DEBUG, GF_LOG_MMIO, ("[OSS] uses %d channels for %d desired ones\n", i, *NbChannels));
        *NbChannels = i;
        if(ioctl(ctx->audio_dev, SNDCTL_DSP_SPEED,&ctx->sr)==-1) return GF_OK;
        if(ioctl(ctx->audio_dev,SNDCTL_DSP_CHANNELS, &ctx->nb_ch)==-1) return GF_OK;
        return GF_OK;
#endif
}

void *NewOSSRender()
{
        OSSContext *ctx;
        GF_AudioOutput*driv;
        ctx = gf_malloc(sizeof(OSSContext));
        if(!ctx)
                return NULL;
        memset(ctx, 0, sizeof(OSSContext));
        driv = gf_malloc(sizeof(GF_AudioOutput));
        if(!driv)
        {
                gf_free(ctx);
                ctx=NULL;
                return NULL;
        }
        memset(driv, 0, sizeof(GF_AudioOutput));
        driv->opaque = ctx;
        driv->SelfThreaded = 0;
        driv->Setup = OSS_Setup;
        driv->Shutdown = OSS_Shutdown;
        driv->ConfigureOutput = OSS_ConfigureOutput;
        driv->GetAudioDelay = OSS_GetAudioDelay;
        driv->SetVolume = OSS_SetVolume;
        driv->SetPan = OSS_SetPan;
        driv->SetPriority = OSS_SetPriority;
        driv->QueryOutputSampleRate = OSS_QueryOutputSampleRate;
        driv->WriteAudio = OSS_WriteAudio;

        GF_REGISTER_MODULE_INTERFACE(driv, GF_AUDIO_OUTPUT_INTERFACE, "OSS Audio Output", "gpac distribution");
        return driv;
}

void DeleteOSSRender(void *ifce)
{
        GF_AudioOutput*dr = (GF_AudioOutput*) ifce;
        OSSContext *ctx = (OSSContext *)dr->opaque;
        gf_free(ctx);
        gf_free(dr);
}


/*
 * ********************************************************************
 * interface
 */
GPAC_MODULE_EXPORT
const u32 *QueryInterfaces()
{
        static u32 si [] = {
                GF_AUDIO_OUTPUT_INTERFACE,
                0
        };
        return si;
}

GPAC_MODULE_EXPORT
GF_BaseInterface *LoadInterface(u32 InterfaceType)
{
        if (InterfaceType == GF_AUDIO_OUTPUT_INTERFACE)
                return NewOSSRender();
        return NULL;
}

GPAC_MODULE_EXPORT
void ShutdownInterface(GF_BaseInterface *ifce)
{
        if (ifce->InterfaceType==GF_AUDIO_OUTPUT_INTERFACE)
                DeleteOSSRender((GF_AudioOutput*)ifce);
}

GPAC_MODULE_STATIC_DECLARATION( oss )

/* [<][>][^][v][top][bottom][index][help] */