mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-03-25 00:22:35 +00:00
254 lines
6.8 KiB
C
254 lines
6.8 KiB
C
/*
|
|
** Copyright (C) 2002-2012 Erik de Castro Lopo <erikd@mega-nerd.com>
|
|
** Copyright (C) 2007 John ffitch
|
|
**
|
|
** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "sfconfig.h"
|
|
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
#include <math.h>
|
|
|
|
#if HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "sndfile.h"
|
|
#include "sfendian.h"
|
|
#include "common.h"
|
|
|
|
#if HAVE_EXTERNAL_LIBS
|
|
|
|
#include <ogg/ogg.h>
|
|
|
|
#include "ogg.h"
|
|
|
|
static int ogg_close (SF_PRIVATE *psf) ;
|
|
static int ogg_stream_classify (SF_PRIVATE *psf, OGG_PRIVATE * odata) ;
|
|
static int ogg_page_classify (SF_PRIVATE * psf, const ogg_page * og) ;
|
|
|
|
int
|
|
ogg_open (SF_PRIVATE *psf)
|
|
{ OGG_PRIVATE* odata = calloc (1, sizeof (OGG_PRIVATE)) ;
|
|
sf_count_t pos = psf_ftell (psf) ;
|
|
int error = 0 ;
|
|
|
|
psf->container_data = odata ;
|
|
psf->container_close = ogg_close ;
|
|
|
|
if (psf->file.mode == SFM_RDWR)
|
|
return SFE_BAD_MODE_RW ;
|
|
|
|
if (psf->file.mode == SFM_READ)
|
|
if ((error = ogg_stream_classify (psf, odata)) != 0)
|
|
return error ;
|
|
|
|
/* Reset everything to an initial state. */
|
|
ogg_sync_clear (&odata->osync) ;
|
|
ogg_stream_clear (&odata->ostream) ;
|
|
psf_fseek (psf, pos, SEEK_SET) ;
|
|
|
|
if (SF_ENDIAN (psf->sf.format) != 0)
|
|
return SFE_BAD_ENDIAN ;
|
|
|
|
switch (psf->sf.format)
|
|
{ case SF_FORMAT_OGG | SF_FORMAT_VORBIS :
|
|
return ogg_vorbis_open (psf) ;
|
|
|
|
case SF_FORMAT_OGGFLAC :
|
|
free (psf->container_data) ;
|
|
psf->container_data = NULL ;
|
|
psf->container_close = NULL ;
|
|
return flac_open (psf) ;
|
|
|
|
#if ENABLE_EXPERIMENTAL_CODE
|
|
case SF_FORMAT_OGG | SF_FORMAT_SPEEX :
|
|
return ogg_speex_open (psf) ;
|
|
|
|
case SF_FORMAT_OGG | SF_FORMAT_PCM_16 :
|
|
case SF_FORMAT_OGG | SF_FORMAT_PCM_24 :
|
|
return ogg_pcm_open (psf) ;
|
|
#endif
|
|
|
|
default :
|
|
break ;
|
|
} ;
|
|
|
|
psf_log_printf (psf, "%s : bad psf->sf.format 0x%x.\n", __func__, psf->sf.format) ;
|
|
return SFE_INTERNAL ;
|
|
} /* ogg_open */
|
|
|
|
|
|
static int
|
|
ogg_close (SF_PRIVATE *psf)
|
|
{ OGG_PRIVATE* odata = psf->container_data ;
|
|
|
|
ogg_sync_clear (&odata->osync) ;
|
|
ogg_stream_clear (&odata->ostream) ;
|
|
|
|
return 0 ;
|
|
} /* ogg_close */
|
|
|
|
static int
|
|
ogg_stream_classify (SF_PRIVATE *psf, OGG_PRIVATE* odata)
|
|
{ char *buffer ;
|
|
int bytes, nn ;
|
|
|
|
/* Call this here so it only gets called once, so no memory is leaked. */
|
|
ogg_sync_init (&odata->osync) ;
|
|
|
|
odata->eos = 0 ;
|
|
|
|
/* Weird stuff happens if these aren't called. */
|
|
ogg_stream_reset (&odata->ostream) ;
|
|
ogg_sync_reset (&odata->osync) ;
|
|
|
|
/*
|
|
** Grab some data at the head of the stream. We want the first page
|
|
** (which is guaranteed to be small and only contain the Vorbis
|
|
** stream initial header) We need the first page to get the stream
|
|
** serialno.
|
|
*/
|
|
|
|
/* Expose the buffer */
|
|
buffer = ogg_sync_buffer (&odata->osync, 4096L) ;
|
|
|
|
/* Grab the part of the header that has already been read. */
|
|
memcpy (buffer, psf->header, psf->headindex) ;
|
|
bytes = psf->headindex ;
|
|
|
|
/* Submit a 4k block to libvorbis' Ogg layer */
|
|
bytes += psf_fread (buffer + psf->headindex, 1, 4096 - psf->headindex, psf) ;
|
|
ogg_sync_wrote (&odata->osync, bytes) ;
|
|
|
|
/* Get the first page. */
|
|
if ((nn = ogg_sync_pageout (&odata->osync, &odata->opage)) != 1)
|
|
{
|
|
/* Have we simply run out of data? If so, we're done. */
|
|
if (bytes < 4096)
|
|
return 0 ;
|
|
|
|
/* Error case. Must not be Vorbis data */
|
|
psf_log_printf (psf, "Input does not appear to be an Ogg bitstream.\n") ;
|
|
return SFE_MALFORMED_FILE ;
|
|
} ;
|
|
|
|
/*
|
|
** Get the serial number and set up the rest of decode.
|
|
** Serialno first ; use it to set up a logical stream.
|
|
*/
|
|
ogg_stream_clear (&odata->ostream) ;
|
|
ogg_stream_init (&odata->ostream, ogg_page_serialno (&odata->opage)) ;
|
|
|
|
if (ogg_stream_pagein (&odata->ostream, &odata->opage) < 0)
|
|
{ /* Error ; stream version mismatch perhaps. */
|
|
psf_log_printf (psf, "Error reading first page of Ogg bitstream data\n") ;
|
|
return SFE_MALFORMED_FILE ;
|
|
} ;
|
|
|
|
if (ogg_stream_packetout (&odata->ostream, &odata->opacket) != 1)
|
|
{ /* No page? must not be vorbis. */
|
|
psf_log_printf (psf, "Error reading initial header packet.\n") ;
|
|
return SFE_MALFORMED_FILE ;
|
|
} ;
|
|
|
|
odata->codec = ogg_page_classify (psf, &odata->opage) ;
|
|
|
|
switch (odata->codec)
|
|
{ case OGG_VORBIS :
|
|
psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS ;
|
|
return 0 ;
|
|
|
|
case OGG_FLAC :
|
|
case OGG_FLAC0 :
|
|
psf->sf.format = SF_FORMAT_OGGFLAC ;
|
|
return 0 ;
|
|
|
|
case OGG_SPEEX :
|
|
psf->sf.format = SF_FORMAT_OGG | SF_FORMAT_SPEEX ;
|
|
return 0 ;
|
|
|
|
case OGG_PCM :
|
|
psf_log_printf (psf, "Detected Ogg/PCM data. This is not supported yet.\n") ;
|
|
return SFE_UNIMPLEMENTED ;
|
|
|
|
default :
|
|
break ;
|
|
} ;
|
|
|
|
psf_log_printf (psf, "This Ogg bitstream contains some uknown data type.\n") ;
|
|
return SFE_UNIMPLEMENTED ;
|
|
} /* ogg_stream_classify */
|
|
|
|
/*==============================================================================
|
|
*/
|
|
|
|
static struct
|
|
{ const char *str, *name ;
|
|
int len, codec ;
|
|
} codec_lookup [] =
|
|
{ { "Annodex", "Annodex", 8, OGG_ANNODEX },
|
|
{ "AnxData", "AnxData", 7, OGG_ANXDATA },
|
|
{ "\177FLAC", "Flac1", 5, OGG_FLAC },
|
|
{ "fLaC", "Flac0", 4, OGG_FLAC0 },
|
|
{ "PCM ", "PCM", 8, OGG_PCM },
|
|
{ "Speex", "Speex", 5, OGG_SPEEX },
|
|
{ "\001vorbis", "Vorbis", 7, OGG_VORBIS },
|
|
} ;
|
|
|
|
static int
|
|
ogg_page_classify (SF_PRIVATE * psf, const ogg_page * og)
|
|
{ int k, len ;
|
|
|
|
for (k = 0 ; k < ARRAY_LEN (codec_lookup) ; k++)
|
|
{ if (codec_lookup [k].len > og->body_len)
|
|
continue ;
|
|
|
|
if (memcmp (og->body, codec_lookup [k].str, codec_lookup [k].len) == 0)
|
|
{ psf_log_printf (psf, "Ogg stream data : %s\n", codec_lookup [k].name) ;
|
|
psf_log_printf (psf, "Stream serialno : %u\n", (uint32_t) ogg_page_serialno (og)) ;
|
|
return codec_lookup [k].codec ;
|
|
} ;
|
|
} ;
|
|
|
|
len = og->body_len < 8 ? og->body_len : 8 ;
|
|
|
|
psf_log_printf (psf, "Ogg_stream data : '") ;
|
|
for (k = 0 ; k < len ; k++)
|
|
psf_log_printf (psf, "%c", isprint (og->body [k]) ? og->body [k] : '.') ;
|
|
psf_log_printf (psf, "' ") ;
|
|
for (k = 0 ; k < len ; k++)
|
|
psf_log_printf (psf, " %02x", og->body [k] & 0xff) ;
|
|
psf_log_printf (psf, "\n") ;
|
|
|
|
return 0 ;
|
|
} /* ogg_page_classify */
|
|
|
|
#else /* HAVE_EXTERNAL_LIBS */
|
|
|
|
int
|
|
ogg_open (SF_PRIVATE *psf)
|
|
{
|
|
psf_log_printf (psf, "This version of libsndfile was compiled without Ogg/Vorbis support.\n") ;
|
|
return SFE_UNIMPLEMENTED ;
|
|
} /* ogg_open */
|
|
|
|
#endif
|