Commit 7ca6e16f authored by Timothy B. Terriberry's avatar Timothy B. Terriberry Committed by Jean-Baptiste Kempf

Opus decoder.

Manually backported from af480570Signed-off-by: default avatarJean-Baptiste Kempf <jb@videolan.org>
parent 94d204fb
...@@ -5,6 +5,9 @@ Audio Output: ...@@ -5,6 +5,9 @@ Audio Output:
* Improve playback synchronization with PulseAudio. * Improve playback synchronization with PulseAudio.
* Fix ALSA digital pass-through ("S/PDIF"). * Fix ALSA digital pass-through ("S/PDIF").
Codecs:
* Support for OPUS via libopus.
Windows: Windows:
* Fix Wallpaper mode on Windows 7 * Fix Wallpaper mode on Windows 7
......
...@@ -639,7 +639,7 @@ AC_CHECK_FUNC(getopt_long,, [ ...@@ -639,7 +639,7 @@ AC_CHECK_FUNC(getopt_long,, [
AC_SUBST(GNUGETOPT_LIBS) AC_SUBST(GNUGETOPT_LIBS)
AC_CHECK_LIB(m,cos,[ AC_CHECK_LIB(m,cos,[
VLC_ADD_LIBS([adjust wave ripple psychedelic gradient a52tofloat32 dtstofloat32 x264 goom visual panoramix rotate noise grain scene kate flac lua chorus_flanger freetype avcodec avformat access_avio swscale postproc i420_rgb faad twolame equalizer spatializer param_eq samplerate freetype mod mpc dmo quicktime realvideo qt4 compressor headphone_channel_mixer normvol audiobargraph_a speex mono colorthres extract ball access_imem hotkeys mosaic gaussianblur dbus x264 hqdn3d],[-lm]) VLC_ADD_LIBS([adjust wave ripple psychedelic gradient a52tofloat32 dtstofloat32 x264 goom visual panoramix rotate noise grain scene kate flac lua chorus_flanger freetype avcodec avformat access_avio swscale postproc i420_rgb faad twolame equalizer spatializer param_eq samplerate freetype mod mpc dmo quicktime realvideo qt4 compressor headphone_channel_mixer normvol audiobargraph_a speex opus mono colorthres extract ball access_imem hotkeys mosaic gaussianblur dbus x264 hqdn3d],[-lm])
LIBM="-lm" LIBM="-lm"
], [ ], [
LIBM="" LIBM=""
...@@ -2810,6 +2810,11 @@ AS_IF([test "${enable_speex}" != "no"], [ ...@@ -2810,6 +2810,11 @@ AS_IF([test "${enable_speex}" != "no"], [
]) ])
AM_CONDITIONAL([HAVE_SPEEXDSP], [test "$have_speexdsp" = "yes"]) AM_CONDITIONAL([HAVE_SPEEXDSP], [test "$have_speexdsp" = "yes"])
dnl
dnl Opus plugin
dnl
PKG_ENABLE_MODULES_VLC([OPUS], [], [ogg opus], [Opus support], [auto])
dnl dnl
dnl theora decoder plugin dnl theora decoder plugin
dnl dnl
......
...@@ -42,7 +42,7 @@ complete list of available options. ...@@ -42,7 +42,7 @@ complete list of available options.
.B VLC .B VLC
recognizes several URL-style items: recognizes several URL-style items:
.TP .TP
.B *.mpg, *.vob, *.avi, *.mp3, *.ogg .B *.mpg, *.vob, *.avi, *.mp3, *.ogg, *.opus
Various multimedia file formats Various multimedia file formats
.TP .TP
.B dvd://[<device>][@<raw device>][@[<title>][:[<chapter>][:<angle>]]] .B dvd://[<device>][@<raw device>][@[<title>][:[<chapter>][:<angle>]]]
......
...@@ -288,6 +288,7 @@ FunctionEnd ...@@ -288,6 +288,7 @@ FunctionEnd
!insertmacro ${_action} ".mp3" !insertmacro ${_action} ".mp3"
!insertmacro ${_action} ".mpc" !insertmacro ${_action} ".mpc"
!insertmacro ${_action} ".oma" !insertmacro ${_action} ".oma"
!insertmacro ${_action} ".opus"
!insertmacro ${_action} ".oga" !insertmacro ${_action} ".oga"
!insertmacro ${_action} ".qcp" !insertmacro ${_action} ".qcp"
!insertmacro ${_action} ".rmi" !insertmacro ${_action} ".rmi"
......
...@@ -212,6 +212,7 @@ typedef enum vlc_dialog { ...@@ -212,6 +212,7 @@ typedef enum vlc_dialog {
"*.oga;" \ "*.oga;" \
"*.ogg;" \ "*.ogg;" \
"*.oma;" \ "*.oma;" \
"*.opus;" \
"*.qcp;" \ "*.qcp;" \
"*.ra;" \ "*.ra;" \
"*.rmi;" \ "*.rmi;" \
......
...@@ -236,6 +236,7 @@ $Id$ ...@@ -236,6 +236,7 @@ $Id$
* opencv_example: OpenCV example (face identification) * opencv_example: OpenCV example (face identification)
* opencv_wrapper: OpenCV wrapper video filter * opencv_wrapper: OpenCV wrapper video filter
* opensles_android: OpenSL ES audio output for Android * opensles_android: OpenSL ES audio output for Android
* opus: a opus audio decoder/packetizer using the libopus library
* osd_parser: OSD import module * osd_parser: OSD import module
* osdmenu: video_filter for displaying and streaming a On Screen Display menu * osdmenu: video_filter for displaying and streaming a On Screen Display menu
* oss: audio output module using the OSS /dev/dsp interface * oss: audio output module using the OSS /dev/dsp interface
......
...@@ -9,6 +9,7 @@ SOURCES_theora = theora.c ...@@ -9,6 +9,7 @@ SOURCES_theora = theora.c
SOURCES_tremor = vorbis.c SOURCES_tremor = vorbis.c
SOURCES_speex = speex.c SOURCES_speex = speex.c
SOURCES_adpcm = adpcm.c SOURCES_adpcm = adpcm.c
SOURCES_opus = opus.c opus_header.c opus_header.h
SOURCES_mpeg_audio = mpeg_audio.c SOURCES_mpeg_audio = mpeg_audio.c
SOURCES_libmpeg2 = libmpeg2.c SOURCES_libmpeg2 = libmpeg2.c
SOURCES_rawvideo = rawvideo.c SOURCES_rawvideo = rawvideo.c
......
/*****************************************************************************
* opus.c: opus decoder/encoder module making use of libopus.
*****************************************************************************
* Copyright (C) 2003-2009, 2012 the VideoLAN team
*
* Authors: Gregory Maxwell <greg@xiph.org>
* Based on speex.c by: Gildas Bazin <gbazin@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.
*****************************************************************************/
/*
* TODO: preskip, trimming, file duration
*/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_input.h>
#include <vlc_codec.h>
#include <vlc_aout.h>
#include "../demux/xiph.h"
#include <ogg/ogg.h>
#include <opus.h>
#include <opus_multistream.h>
#include "opus_header.h"
#ifndef OPUS_SET_GAIN
#include <math.h>
#endif
/*****************************************************************************
* Module descriptor
*****************************************************************************/
static int OpenDecoder ( vlc_object_t * );
static void CloseDecoder ( vlc_object_t * );
vlc_module_begin ()
set_category( CAT_INPUT )
set_subcategory( SUBCAT_INPUT_ACODEC )
set_description( N_("Opus audio decoder") )
set_capability( "decoder", 100 )
set_shortname( N_("Opus") )
set_callbacks( OpenDecoder, CloseDecoder )
vlc_module_end ()
/*****************************************************************************
* decoder_sys_t : opus decoder descriptor
*****************************************************************************/
struct decoder_sys_t
{
/*
* Input properties
*/
bool b_has_headers;
/*
* Opus properties
*/
OpusHeader header;
OpusMSDecoder *p_st;
/*
* Common properties
*/
date_t end_date;
};
static const int pi_channels_maps[9] =
{
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,
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
| AOUT_CHAN_REARCENTER | AOUT_CHAN_MIDDLELEFT
| AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE,
AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT
| AOUT_CHAN_LFE,
};
/*
** channel order as defined in http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9
*/
/* recommended vorbis channel order for 8 channels */
static const uint32_t pi_8channels_in[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_CENTER, AOUT_CHAN_RIGHT,
AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT,
AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,AOUT_CHAN_LFE, 0 };
/* recommended vorbis channel order for 7 channels */
static const uint32_t pi_7channels_in[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_CENTER, AOUT_CHAN_RIGHT,
AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT,
AOUT_CHAN_REARCENTER, AOUT_CHAN_LFE, 0 };
/* recommended vorbis channel order for 6 channels */
static const uint32_t pi_6channels_in[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_CENTER, AOUT_CHAN_RIGHT,
AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, AOUT_CHAN_LFE, 0 };
/* recommended vorbis channel order for 5 channels */
static const uint32_t pi_5channels_in[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_CENTER, AOUT_CHAN_RIGHT,
AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 };
/* recommended vorbis channel order for 4 channels */
static const uint32_t pi_4channels_in[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, 0 };
/* recommended vorbis channel order for 3 channels */
static const uint32_t pi_3channels_in[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_CENTER, AOUT_CHAN_RIGHT, 0 };
/****************************************************************************
* Local prototypes
****************************************************************************/
static block_t *DecodeBlock ( decoder_t *, block_t ** );
static int ProcessHeaders( decoder_t * );
static int ProcessInitialHeader ( decoder_t *, ogg_packet * );
static void *ProcessPacket( decoder_t *, ogg_packet *, block_t ** );
static block_t *DecodePacket( decoder_t *, ogg_packet *, int, int );
/*****************************************************************************
* OpenDecoder: probe the decoder and return score
*****************************************************************************/
static int OpenDecoder( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*)p_this;
decoder_sys_t *p_sys;
if( p_dec->fmt_in.i_codec != VLC_CODEC_OPUS )
return VLC_EGENERIC;
/* Allocate the memory needed to store the decoder's structure */
if( ( p_dec->p_sys = p_sys = malloc(sizeof(decoder_sys_t)) ) == NULL )
return VLC_ENOMEM;
p_dec->p_sys->b_has_headers = false;
date_Set( &p_sys->end_date, 0 );
/* Set output properties */
p_dec->fmt_out.i_cat = AUDIO_ES;
p_dec->fmt_out.i_codec = VLC_CODEC_FL32;
p_dec->pf_decode_audio = DecodeBlock;
p_dec->pf_packetize = DecodeBlock;
p_sys->p_st = NULL;
return VLC_SUCCESS;
}
/****************************************************************************
* DecodeBlock: the whole thing
****************************************************************************
* This function must be fed with ogg packets.
****************************************************************************/
static block_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
ogg_packet oggpacket;
if( !pp_block || !*pp_block)
return NULL;
/* Block to Ogg packet */
oggpacket.packet = (*pp_block)->p_buffer;
oggpacket.bytes = (*pp_block)->i_buffer;
oggpacket.granulepos = -1;
oggpacket.b_o_s = 0;
oggpacket.e_o_s = 0;
oggpacket.packetno = 0;
/* Check for headers */
if( !p_sys->b_has_headers )
{
if( ProcessHeaders( p_dec ) )
{
block_Release( *pp_block );
return NULL;
}
p_sys->b_has_headers = true;
}
return ProcessPacket( p_dec, &oggpacket, pp_block );
}
/*****************************************************************************
* ProcessHeaders: process Opus headers.
*****************************************************************************/
static int ProcessHeaders( decoder_t *p_dec )
{
ogg_packet oggpacket;
unsigned pi_size[XIPH_MAX_HEADER_COUNT];
void *pp_data[XIPH_MAX_HEADER_COUNT];
unsigned i_count;
int ret = VLC_EGENERIC;
if( xiph_SplitHeaders( pi_size, pp_data, &i_count,
p_dec->fmt_in.i_extra, p_dec->fmt_in.p_extra) )
return VLC_EGENERIC;
if( i_count < 2 )
goto end;
oggpacket.granulepos = -1;
oggpacket.e_o_s = 0;
oggpacket.packetno = 0;
/* Take care of the initial Opus header */
oggpacket.b_o_s = 1; /* yes this actually is a b_o_s packet :) */
oggpacket.bytes = pi_size[0];
oggpacket.packet = pp_data[0];
ret = ProcessInitialHeader( p_dec, &oggpacket );
if (ret != VLC_SUCCESS)
msg_Err( p_dec, "initial Opus header is corrupted" );
end:
for( unsigned i = 0; i < i_count; i++ )
free( pp_data[i] );
return ret;
}
/*****************************************************************************
* ProcessInitialHeader: processes the inital Opus header packet.
*****************************************************************************/
static int ProcessInitialHeader( decoder_t *p_dec, ogg_packet *p_oggpacket )
{
int err;
int pi_chan_table[AOUT_CHAN_MAX];
unsigned char new_stream_map[8];
decoder_sys_t *p_sys = p_dec->p_sys;
OpusHeader *p_header = &p_sys->header;
if( !opus_header_parse((unsigned char *)p_oggpacket->packet,p_oggpacket->bytes,p_header) )
{
msg_Err( p_dec, "cannot read Opus header" );
return VLC_EGENERIC;
}
msg_Dbg( p_dec, "Opus audio with %d channels", p_header->channels);
if((p_header->channels>2 && p_header->channel_mapping==0) ||
(p_header->channels>8 && p_header->channel_mapping==1) ||
p_header->channel_mapping>1)
{
msg_Err( p_dec, "Unsupported channel mapping" );
return VLC_EGENERIC;
}
/* Setup the format */
p_dec->fmt_out.audio.i_physical_channels =
p_dec->fmt_out.audio.i_original_channels =
pi_channels_maps[p_header->channels];
p_dec->fmt_out.audio.i_channels = p_header->channels;
p_dec->fmt_out.audio.i_rate = 48000;
if( p_header->channels>2 )
{
static const uint32_t *pi_ch[6] = { pi_3channels_in, pi_4channels_in,
pi_5channels_in, pi_6channels_in,
pi_7channels_in, pi_8channels_in };
aout_CheckChannelReorder( pi_ch[p_header->channels-3], NULL,
p_dec->fmt_out.audio.i_physical_channels,
p_header->channels,
pi_chan_table );
for(int i=0;i<p_header->channels;i++)
new_stream_map[pi_chan_table[i]]=p_header->stream_map[i];
}
/* Opus decoder init */
p_sys->p_st = opus_multistream_decoder_create( 48000, p_header->channels,
p_header->nb_streams, p_header->nb_coupled,
p_header->channels>2?new_stream_map:p_header->stream_map,
&err );
if( !p_sys->p_st || err!=OPUS_OK )
{
msg_Err( p_dec, "decoder initialization failed" );
return VLC_EGENERIC;
}
#ifdef OPUS_SET_GAIN
if( opus_multistream_decoder_ctl( p_sys->p_st,OPUS_SET_GAIN(p_header->gain) ) != OPUS_OK )
{
msg_Err( p_dec, "OPUS_SET_GAIN failed" );
opus_multistream_decoder_destroy( p_sys->p_st );
return VLC_EGENERIC;
}
#endif
date_Init( &p_sys->end_date, 48000, 1 );
return VLC_SUCCESS;
}
/*****************************************************************************
* ProcessPacket: processes a Opus packet.
*****************************************************************************/
static void *ProcessPacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
block_t **pp_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_block = *pp_block;
/* Date management */
if( p_block && p_block->i_pts > VLC_TS_INVALID &&
p_block->i_pts != date_Get( &p_sys->end_date ) )
{
date_Set( &p_sys->end_date, p_block->i_pts );
}
if( !date_Get( &p_sys->end_date ) )
{
/* We've just started the stream, wait for the first PTS. */
if( p_block ) block_Release( p_block );
return NULL;
}
*pp_block = NULL; /* To avoid being fed the same packet again */
{
block_t *p_aout_buffer = DecodePacket( p_dec, p_oggpacket,
p_block->i_nb_samples,
(int)p_block->i_length );
block_Release( p_block );
return p_aout_buffer;
}
}
/*****************************************************************************
* DecodePacket: decodes a Opus packet.
*****************************************************************************/
static block_t *DecodePacket( decoder_t *p_dec, ogg_packet *p_oggpacket,
int i_nb_samples, int i_end_trim )
{
decoder_sys_t *p_sys = p_dec->p_sys;
if( !p_oggpacket->bytes )
return NULL;
int spp;
spp=opus_packet_get_nb_frames(p_oggpacket->packet,p_oggpacket->bytes);
if(spp>0)spp*=opus_packet_get_samples_per_frame(p_oggpacket->packet,48000);
if(spp<120||spp>120*48)return NULL;
block_t *p_aout_buffer=decoder_NewAudioBuffer( p_dec, spp );
if ( !p_aout_buffer )
{
msg_Err(p_dec, "Oops: No new buffer was returned!");
return NULL;
}
spp=opus_multistream_decode_float(p_sys->p_st, p_oggpacket->packet,
p_oggpacket->bytes, (float *)p_aout_buffer->p_buffer, spp, 0);
if( spp < 0 || i_nb_samples <= 0 || i_end_trim >= i_nb_samples)
{
block_Release(p_aout_buffer);
if( spp < 0 )
msg_Err( p_dec, "Error: corrupted stream?" );
return NULL;
}
if( spp > i_nb_samples )
{
memmove(p_aout_buffer->p_buffer,
p_aout_buffer->p_buffer
+ (spp - i_nb_samples)*p_sys->header.channels*sizeof(float),
(i_nb_samples - i_end_trim)*p_sys->header.channels*sizeof(float));
}
i_nb_samples -= i_end_trim;
#ifndef OPUS_SET_GAIN
if(p_sys->header.gain!=0)
{
float gain = pow(10., p_sys->header.gain/5120.);
float *buf =(float *)p_aout_buffer->p_buffer;
int i;
for( i = 0; i < i_nb_samples*p_sys->header.channels; i++)
buf[i] *= gain;
}
#endif
p_aout_buffer->i_nb_samples = i_nb_samples;
p_aout_buffer->i_pts = date_Get( &p_sys->end_date );
p_aout_buffer->i_length = date_Increment( &p_sys->end_date, i_nb_samples )
- p_aout_buffer->i_pts;
return p_aout_buffer;
}
/*****************************************************************************
* CloseDecoder: Opus decoder destruction
*****************************************************************************/
static void CloseDecoder( vlc_object_t *p_this )
{
decoder_t * p_dec = (decoder_t *)p_this;
decoder_sys_t *p_sys = p_dec->p_sys;
if( p_sys->p_st ) opus_multistream_decoder_destroy(p_sys->p_st);
free( p_sys );
}
/* Copyright (C)2012 Xiph.Org Foundation
File: opus_header.c
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "opus_header.h"
#include <string.h>
#include <stdio.h>
/* Header contents:
- "OpusHead" (64 bits)
- version number (8 bits)
- Channels C (8 bits)
- Pre-skip (16 bits)
- Sampling rate (32 bits)
- Gain in dB (16 bits, S7.8)
- Mapping (8 bits, 0=single stream (mono/stereo) 1=Vorbis mapping,
2..254: reserved, 255: multistream with no mapping)
- if (mapping != 0)
- N = totel number of streams (8 bits)
- M = number of paired streams (8 bits)
- C times channel origin
- if (C<2*M)
- stream = byte/2
- if (byte&0x1 == 0)
- left
else
- right
- else
- stream = byte-M
*/
typedef struct {
unsigned char *data;
int maxlen;
int pos;
} Packet;
typedef struct {
const unsigned char *data;
int maxlen;
int pos;
} ROPacket;
static int write_uint32(Packet *p, ogg_uint32_t val)
{
if (p->pos>p->maxlen-4)
return 0;
p->data[p->pos ] = (val ) & 0xFF;
p->data[p->pos+1] = (val>> 8) & 0xFF;
p->data[p->pos+2] = (val>>16) & 0xFF;
p->data[p->pos+3] = (val>>24) & 0xFF;
p->pos += 4;
return 1;
}
static int write_uint16(Packet *p, ogg_uint16_t val)
{
if (p->pos>p->maxlen-2)
return 0;
p->data[p->pos ] = (val ) & 0xFF;
p->data[p->pos+1] = (val>> 8) & 0xFF;
p->pos += 2;
return 1;
}
static int write_chars(Packet *p, const unsigned char *str, int nb_chars)
{
if (p->pos>p->maxlen-nb_chars)
return 0;
for (int i=0;i<nb_chars;i++)
p->data[p->pos++] = str[i];
return 1;
}
static int read_uint32(ROPacket *p, ogg_uint32_t *val)
{
if (p->pos>p->maxlen-4)
return 0;
*val = (ogg_uint32_t)p->data[p->pos ];
*val |= (ogg_uint32_t)p->data[p->pos+1]<< 8;
*val |= (ogg_uint32_t)p->data[p->pos+2]<<16;
*val |= (ogg_uint32_t)p->data[p->pos+3]<<24;
p->pos += 4;
return 1;
}
static int read_uint16(ROPacket *p, ogg_uint16_t *val)
{
if (p->pos>p->maxlen-2)
return 0;
*val = (ogg_uint16_t)p->data[p->pos ];
*val |= (ogg_uint16_t)p->data[p->pos+1]<<8;
p->pos += 2;
return 1;
}
static int read_chars(ROPacket *p, unsigned char *str, int nb_chars)
{
if (p->pos>p->maxlen-nb_chars)
return 0;
for (int i=0;i<nb_chars;i++)
str[i] = p->data[p->pos++];
return 1;
}
int opus_header_parse(const unsigned char *packet, int len, OpusHeader *h)
{
char str[9];
ROPacket p;
unsigned char ch;
ogg_uint16_t shortval;
p.data = packet;
p.maxlen = len;
p.pos = 0;
str[8] = 0;
if (len<19)return 0;
read_chars(&p, (unsigned char*)str, 8);
if (memcmp(str, "OpusHead", 8)!=0)
return 0;
if (!read_chars(&p, &ch, 1))
return 0;
h->version = ch;
if((h->version&240) != 0) /* Only major version 0 supported. */
return 0;
if (!read_chars(&p, &ch, 1))
return 0;
h->channels = ch;
if (h->channels == 0)
return 0;
if (!read_uint16(&p, &shortval))
return 0;
h->preskip = shortval;
if (!read_uint32(&p, &h->input_sample_rate))
return 0;
if (!read_uint16(&p, &shortval))
return 0;
h->gain = (short)shortval;
if (!read_chars(&p, &ch, 1))
return 0;
h->channel_mapping = ch;
if (h->channel_mapping != 0)
{
if (!read_chars(&p, &ch, 1))
return 0;
if (ch<1)
return 0;
h->nb_streams = ch;
if (!read_chars(&p, &ch, 1))
return 0;
if (ch>h->nb_streams || (ch+h->nb_streams)>255)
return 0;
h->nb_coupled = ch;
/* Multi-stream support */
for (int i=0;i<h->channels;i++)
{
if (!read_chars(&p, &h->stream_map[i], 1))
return 0;
if (h->stream_map[i]>(h->nb_streams+h->nb_coupled) && h->stream_map[i]!=255)
return 0;
}
} else {
if(h->channels>2)
return 0;
h->nb_streams = 1;
h->nb_coupled = h->channels>1;
h->stream_map[0]=0;
h->stream_map[1]=1;
}
/*For version 0/1 we know there won't be any more data
so reject any that have data past the end.*/
if ((h->version==0 || h->version==1) && p.pos != len)
return 0;
return 1;
}
int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int len)
{
Packet p;
unsigned char ch;
p.data = packet;
p.maxlen = len;
p.pos = 0;
if (len<19)return 0;
if (!write_chars(&p, (const unsigned char*)"OpusHead", 8))
return 0;
/* Version is 1 */
ch = 1;
if (!write_chars(&p, &ch, 1))
return 0;
ch = h->channels;
if (!write_chars(&p, &ch, 1))
return 0;
if (!write_uint16(&p, h->preskip))
return 0;
if (!write_uint32(&p, h->input_sample_rate))
return 0;
if (!write_uint16(&p, h->gain))
return 0;
ch = h->channel_mapping;
if (!write_chars(&p, &ch, 1))
return 0;
if (h->channel_mapping != 0)
{
ch = h->nb_streams;
if (!write_chars(&p, &ch, 1))
return 0;
ch = h->nb_coupled;
if (!write_chars(&p, &ch, 1))
return 0;
/* Multi-stream support */
for (int i=0;i<h->channels;i++)
{
if (!write_chars(&p, &h->stream_map[i], 1))
return 0;
}
}
return p.pos;
}
/* Copyright (C)2012 Xiph.Org Foundation
File: opus_header.h
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef OPUS_HEADER_H
#define OPUS_HEADER_H
#include <ogg/ogg.h>
typedef struct {
int version;
int channels; /* Number of channels: 1..255 */
int preskip;
ogg_uint32_t input_sample_rate;
int gain; /* in dB S7.8 should be zero whenever possible */
int channel_mapping;
/* The rest is only used if channel_mapping != 0 */
int nb_streams;
int nb_coupled;
unsigned char stream_map[255];
} OpusHeader;
int opus_header_parse(const unsigned char *header, int len, OpusHeader *h);
int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int len);
#endif
...@@ -974,8 +974,8 @@ void SPrefsPanel::assoDialog() ...@@ -974,8 +974,8 @@ void SPrefsPanel::assoDialog()
aTa( ".a52" ); aTa( ".aac" ); aTa( ".ac3" ); aTa( ".dts" ); aTa( ".flac" ); aTa( ".a52" ); aTa( ".aac" ); aTa( ".ac3" ); aTa( ".dts" ); aTa( ".flac" );
aTa( ".m4a" ); aTa( ".m4p" ); aTa( ".mka" ); aTa( ".mod" ); aTa( ".mp1" ); aTa( ".m4a" ); aTa( ".m4p" ); aTa( ".mka" ); aTa( ".mod" ); aTa( ".mp1" );
aTa( ".mp2" ); aTa( ".mp3" ); aTa( ".oma" ); aTa( ".oga" ); aTa( ".spx" ); aTa( ".mp2" ); aTa( ".mp3" ); aTa( ".oma" ); aTa( ".oga" ); aTa( ".opus" );
aTa( ".tta" ); aTa( ".wav" ); aTa( ".wma" ); aTa( ".xm" ); aTa( ".spx" ); aTa( ".tta" ); aTa( ".wav" ); aTa( ".wma" ); aTa( ".xm" );
audioType->setCheckState( 0, ( i_temp > 0 ) ? audioType->setCheckState( 0, ( i_temp > 0 ) ?
( ( i_temp == audioType->childCount() ) ? ( ( i_temp == audioType->childCount() ) ?
Qt::Checked : Qt::PartiallyChecked ) Qt::Checked : Qt::PartiallyChecked )
......
...@@ -132,6 +132,7 @@ demux_t *demux_New( vlc_object_t *p_obj, input_thread_t *p_parent_input, ...@@ -132,6 +132,7 @@ demux_t *demux_New( vlc_object_t *p_obj, input_thread_t *p_parent_input,
{ "ogg", "ogg" }, { "ogm", "ogg" }, /* legacy Ogg */ { "ogg", "ogg" }, { "ogm", "ogg" }, /* legacy Ogg */
{ "oga", "ogg" }, { "spx", "ogg" }, { "ogv", "ogg" }, { "oga", "ogg" }, { "spx", "ogg" }, { "ogv", "ogg" },
{ "ogx", "ogg" }, /*RFC5334*/ { "ogx", "ogg" }, /*RFC5334*/
{ "opus", "ogg" }, /*draft-terriberry-oggopus-01*/
{ "pva", "pva" }, { "pva", "pva" },
{ "rm", "avformat" }, { "rm", "avformat" },
{ "m4v", "m4v" }, { "m4v", "m4v" },
......
...@@ -217,6 +217,7 @@ static const struct ...@@ -217,6 +217,7 @@ static const struct
{ ".ogm", "application/ogg" }, { ".ogm", "application/ogg" },
{ ".ogv", "video/ogg" }, { ".ogv", "video/ogg" },
{ ".ogx", "application/ogg" }, { ".ogx", "application/ogg" },
{ ".opus", "audio/ogg; codecs=opus" },
{ ".spx", "audio/ogg" }, { ".spx", "audio/ogg" },
{ ".wav", "audio/wav" }, { ".wav", "audio/wav" },
{ ".wma", "audio/x-ms-wma" }, { ".wma", "audio/x-ms-wma" },
......
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