diff --git a/ext/vorbis/Makefile.am b/ext/vorbis/Makefile.am new file mode 100644 index 0000000000..5a87e9ec52 --- /dev/null +++ b/ext/vorbis/Makefile.am @@ -0,0 +1,9 @@ +plugindir = $(libdir)/gst + +plugin_LTLIBRARIES = libgstvorbis.la + +libgstvorbis_la_SOURCES = vorbis.c vorbisenc.c vorbisdec.c +libgstvorbis_la_LIBADD = $(VORBIS_LIBS) +libgstvorbis_la_CFLAGS = $(VORBIS_CFLAGS) $(GST_CFLAGS) + +noinst_HEADERS = vorbisenc.h vorbisdec.h diff --git a/ext/vorbis/vorbis.c b/ext/vorbis/vorbis.c new file mode 100644 index 0000000000..b4d58d121b --- /dev/null +++ b/ext/vorbis/vorbis.c @@ -0,0 +1,158 @@ +/* Gnome-Streamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#include +#include + +extern GstElementDetails vorbisenc_details; +extern GstElementDetails vorbisdec_details; + +static GstCaps* vorbis_typefind (GstBuffer *buf, gpointer private); + +GstPadTemplate *dec_src_template, *dec_sink_template; +GstPadTemplate *enc_src_template, *enc_sink_template; + +static GstCaps* +vorbis_caps_factory (void) +{ + return + gst_caps_new ( + "vorbis_vorbis", + "audio/x-ogg", + NULL); +} + +static GstCaps* +raw_caps_factory (void) +{ + return + gst_caps_new ( + "vorbis_raw", + "audio/raw", + gst_props_new ( + "format", GST_PROPS_STRING ("int"), + "law", GST_PROPS_INT (0), + "endianness", GST_PROPS_INT (G_BYTE_ORDER), + "signed", GST_PROPS_BOOLEAN (TRUE), + "width", GST_PROPS_INT (16), + "depth", GST_PROPS_INT (16), + "rate", GST_PROPS_INT_RANGE (11025, 48000), + "channels", GST_PROPS_INT_RANGE (1, 2), + NULL)); +} + +static GstCaps* +raw_caps2_factory (void) +{ + return + gst_caps_new ( + "vorbis_raw_float", + "audio/raw", + gst_props_new ( + "format", GST_PROPS_STRING ("float"), + "layout", GST_PROPS_STRING ("IEEE"), + "rate", GST_PROPS_INT_RANGE (11025, 48000), + "channels", GST_PROPS_INT (2), + NULL)); +} + +static GstTypeDefinition vorbisdefinition = { + "vorbis_audio/x-ogg", + "audio/x-ogg", + ".ogg", + vorbis_typefind, +}; + +static GstCaps* +vorbis_typefind (GstBuffer *buf, gpointer private) +{ + gulong head = GULONG_FROM_BE (*((gulong *)GST_BUFFER_DATA (buf))); + + if (head != 0x4F676753) + return NULL; + + return gst_caps_new ("vorbis_typefind", "audio/x-ogg", NULL); +} + + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *enc, *dec; + GstTypeFactory *type; + GstCaps *raw_caps, *vorbis_caps, *raw_caps2; + + gst_plugin_set_longname (plugin, "The OGG Vorbis Codec"); + + /* create an elementfactory for the vorbisenc element */ + enc = gst_elementfactory_new ("vorbisenc", GST_TYPE_VORBISENC, + &vorbisenc_details); + g_return_val_if_fail (enc != NULL, FALSE); + + raw_caps = raw_caps_factory (); + raw_caps2 = raw_caps2_factory (); + vorbis_caps = vorbis_caps_factory (); + + /* register sink pads */ + enc_sink_template = gst_padtemplate_new ("sink", GST_PAD_SINK, + GST_PAD_ALWAYS, + raw_caps, NULL); + gst_elementfactory_add_padtemplate (enc, enc_sink_template); + + /* register src pads */ + enc_src_template = gst_padtemplate_new ("src", GST_PAD_SRC, + GST_PAD_ALWAYS, + vorbis_caps, NULL); + gst_elementfactory_add_padtemplate (enc, enc_src_template); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (enc)); + + /* create an elementfactory for the vorbisdec element */ + dec = gst_elementfactory_new("vorbisdec",GST_TYPE_VORBISDEC, + &vorbisdec_details); + g_return_val_if_fail(dec != NULL, FALSE); + + /* register sink pads */ + dec_sink_template = gst_padtemplate_new ("sink", GST_PAD_SINK, + GST_PAD_ALWAYS, + vorbis_caps, NULL); + gst_elementfactory_add_padtemplate (dec, dec_sink_template); + + raw_caps = gst_caps_prepend (raw_caps, raw_caps2); + /* register src pads */ + dec_src_template = gst_padtemplate_new ("src", GST_PAD_SRC, + GST_PAD_ALWAYS, + raw_caps, NULL); + gst_elementfactory_add_padtemplate (dec, dec_src_template); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (dec)); + + type = gst_typefactory_new (&vorbisdefinition); + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "vorbis", + plugin_init +}; diff --git a/ext/vorbis/vorbisdec.c b/ext/vorbis/vorbisdec.c new file mode 100644 index 0000000000..c68cda3309 --- /dev/null +++ b/ext/vorbis/vorbisdec.c @@ -0,0 +1,456 @@ +/* Gnome-Streamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include + +/*#define DEBUG_ENABLED */ +#include + +static void gst_vorbisdec_loop (GstElement *element); + +extern GstPadTemplate *dec_src_template, *dec_sink_template; + +/* elementfactory information */ +GstElementDetails vorbisdec_details = { + "Ogg Vorbis decoder", + "Filter/Audio/Decoder", + "Decodes OGG Vorbis audio", + VERSION, + "Monty , "\ + "Wim Taymans ", + "(C) 2000", +}; + +#define VORBIS_INIT 1 +#define VORBIS_HEADER1 2 +#define VORBIS_HEADER2 3 +#define VORBIS_INFO 4 +#define VORBIS_MAIN_LOOP 5 +#define VORBIS_CLEAN 6 +#define VORBIS_EXIT 7 +#define VORBIS_DONE 8 +#define VORBIS_ERROR 9 + +/* VorbisDec signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, +}; + +static void gst_vorbisdec_class_init (VorbisDecClass *klass); +static void gst_vorbisdec_init (VorbisDec *vorbisdec); + + +static GstElementClass *parent_class = NULL; +/*static guint gst_vorbisdec_signals[LAST_SIGNAL] = { 0 }; */ + +GType +vorbisdec_get_type(void) { + static GType vorbisdec_type = 0; + + if (!vorbisdec_type) { + static const GTypeInfo vorbisdec_info = { + sizeof(VorbisDecClass), NULL, + NULL, + (GClassInitFunc)gst_vorbisdec_class_init, + NULL, + NULL, + sizeof(VorbisDec), + 0, + (GInstanceInitFunc)gst_vorbisdec_init, + }; + vorbisdec_type = g_type_register_static(GST_TYPE_ELEMENT, "VorbisDec", &vorbisdec_info, 0); + } + return vorbisdec_type; +} + +static void +gst_vorbisdec_class_init (VorbisDecClass *klass) +{ + GstElementClass *gstelement_class; + + gstelement_class = (GstElementClass*)klass; + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); +} + +static void +gst_vorbisdec_init (VorbisDec *vorbisdec) +{ + vorbisdec->sinkpad = gst_pad_new_from_template (dec_sink_template, "sink"); + gst_element_add_pad (GST_ELEMENT (vorbisdec), vorbisdec->sinkpad); + + gst_element_set_loop_function (GST_ELEMENT (vorbisdec), gst_vorbisdec_loop); + vorbisdec->srcpad = gst_pad_new_from_template (dec_src_template, "src"); + gst_element_add_pad (GST_ELEMENT (vorbisdec), vorbisdec->srcpad); + + ogg_sync_init (&vorbisdec->oy); /* Now we can read pages */ + vorbisdec->state = VORBIS_INIT; + vorbisdec->convsize = 4096; +} + +static void +gst_vorbisdec_loop (GstElement *element) +{ + VorbisDec *vorbisdec; + GstBuffer *buf; + GstBuffer *outbuf; + + ogg_sync_state oy; /* sync and verify incoming physical bitstream */ + ogg_stream_state os; /* take physical pages, weld into a logical + stream of packets */ + ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */ + ogg_packet op; /* one raw packet of data for decode */ + + vorbis_info vi; /* struct that stores all the static vorbis bitstream + settings */ + vorbis_comment vc; /* struct that stores all the bitstream user comments */ + vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ + vorbis_block vb; /* local working space for packet->PCM decode */ + + char *buffer; + int bytes; + + g_return_if_fail (element != NULL); + g_return_if_fail (GST_IS_VORBISDEC (element)); + + vorbisdec = GST_VORBISDEC (element); + + /********** Decode setup ************/ + ogg_sync_init (&oy); /* Now we can read pages */ + + while(1){ /* we repeat if the bitstream is chained */ + int eos=0; + int i; + gboolean need_sync = FALSE; + + /* 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. */ + + /* FIXME HACK! trap COTHREAD_STOPPING here */ + if (GST_ELEMENT_IS_COTHREAD_STOPPING (vorbisdec)) { + GST_DEBUG(0, "HACK HACK HACK, switching to cothread zero on COTHREAD_STOPPING\n"); + cothread_switch(cothread_current_main()); + } + + /* submit a 4k block to libvorbis' Ogg layer */ + GST_DEBUG (0,"vorbisdec: pull\n"); + buf = gst_pad_pull (vorbisdec->sinkpad); + + if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLUSH)) { + /*g_print("reset\n"); */ + ogg_sync_reset(&oy); + need_sync = TRUE; + } + GST_DEBUG (0,"vorbisdec: pull done\n"); + bytes = GST_BUFFER_SIZE (buf); + + buffer = ogg_sync_buffer (&oy, bytes); + memcpy (buffer, GST_BUFFER_DATA (buf), bytes); + + ogg_sync_wrote(&oy,bytes); + + /* Get the first page. */ + if(ogg_sync_pageout (&oy, &og)!=1){ + /* have we simply run out of data? If so, we're done. */ + if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_EOS)) { + gst_buffer_unref (buf); + break; + } + + /* error case. Must not be Vorbis data */ + GST_DEBUG (0,"Input does not appear to be an Ogg bitstream.\n"); +/* FIXME */ +/* exit(1); */ + } + gst_buffer_unref (buf); + + /* Get the serial number and set up the rest of decode. */ + /* serialno first; use it to set up a logical stream */ + ogg_stream_init (&os, ogg_page_serialno (&og)); + + /* extract the initial header from the first page and verify that the + Ogg bitstream is in fact Vorbis data */ + + /* I handle the initial header first instead of just having the code + read all three Vorbis headers at once because reading the initial + header is an easy way to identify a Vorbis bitstream and it's + useful to see that functionality seperated out. */ + + vorbis_info_init (&vi); + vorbis_comment_init (&vc); + if(ogg_stream_pagein (&os, &og) < 0){ + /* error; stream version mismatch perhaps */ + printf("Error reading first page of Ogg bitstream data.\n"); +/* FIXME */ +/* exit(1); */ + } + + if(ogg_stream_packetout (&os, &op) != 1){ + /* no page? must not be vorbis */ + printf("Error reading initial header packet.\n"); +/* FIXME */ +/* exit(1); */ + } + + if(vorbis_synthesis_headerin (&vi, &vc,&op) < 0){ + /* error case; not a vorbis header */ + printf("This Ogg bitstream does not contain Vorbis " + "audio data.\n"); +/* FIXME */ +/* exit(1); */ + } + + /* At this point, we're sure we're Vorbis. We've set up the logical + (Ogg) bitstream decoder. Get the comment and codebook headers and + set up the Vorbis decoder */ + + /* The next two packets in order are the comment and codebook headers. + They're likely large and may span multiple pages. Thus we reead + and submit data until we get our two pacakets, watching that no + pages are missing. If a page is missing, error out; losing a + header page is the only place where missing data is fatal. */ + + i=0; + while(i<2){ + while(i<2){ + int result = ogg_sync_pageout (&oy, &og); + if (result == 0) break; /* Need more data */ + /* Don't complain about missing or corrupt data yet. We'll + catch it at the packet output phase */ + if (result == 1) { + ogg_stream_pagein (&os, &og); /* we can ignore any errors here + as they'll also become apparent + at packetout */ + while(i<2){ + result = ogg_stream_packetout (&os, &op); + if (result == 0) break; + if (result == -1){ + /* Uh oh; data at some point was corrupted or missing! + We can't tolerate that in a header. Die. */ + printf("Corrupt secondary header. Exiting.\n"); +/* FIXME */ +/* exit(1); */ + } + vorbis_synthesis_headerin (&vi, &vc, &op); + i++; + } + } + } + /* no harm in not checking before adding more */ + /* FIXME HACK! trap COTHREAD_STOPPING here */ + if (GST_ELEMENT_IS_COTHREAD_STOPPING (vorbisdec)) { + GST_DEBUG(0, "HACK HACK HACK, switching to cothread zero on COTHREAD_STOPPING\n"); + cothread_switch(cothread_current_main()); + } + + GST_DEBUG (0,"vorbisdec: pull\n"); + buf = gst_pad_pull (vorbisdec->sinkpad); + if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLUSH)) { + /*g_print("reset\n"); */ + ogg_sync_reset(&oy); + need_sync = TRUE; + } + GST_DEBUG (0,"vorbisdec: pull done\n"); + bytes = GST_BUFFER_SIZE (buf); + buffer = ogg_sync_buffer (&oy, bytes); + memcpy (buffer, GST_BUFFER_DATA (buf), bytes); + gst_buffer_unref (buf); + + if(bytes==0 && i < 2){ + fprintf(stderr,"End of file before finding all Vorbis headers!\n"); +/* FIXME */ +/* exit(1); */ + } + ogg_sync_wrote (&oy, bytes); + } + + /* Throw the comments plus a few lines about the bitstream we're + decoding */ + { + char **ptr = vc.user_comments; + while(*ptr){ + GST_INFO (GST_CAT_PLUGIN_INFO, "vorbisdec: %s\n",*ptr); + ++ptr; + } + GST_INFO (GST_CAT_PLUGIN_INFO, "vorbisdec: Bitstream is %d channel, %ldHz", vi.channels, vi.rate); + GST_INFO (GST_CAT_PLUGIN_INFO, "vorbisdec: Encoded by: %s", vc.vendor); + } + + gst_pad_set_caps (vorbisdec->srcpad, + gst_caps_new ( + "vorbisdec_src", + "audio/raw", + gst_props_new ( + "format", GST_PROPS_STRING ("int"), + "law", GST_PROPS_INT (0), + "endianness", GST_PROPS_INT (G_BYTE_ORDER), + "signed", GST_PROPS_BOOLEAN (TRUE), + "width", GST_PROPS_INT (16), + "depth", GST_PROPS_INT (16), + "rate", GST_PROPS_INT (vi.rate), + "channels", GST_PROPS_INT (vi.channels), + NULL + ))); + + vorbisdec->convsize = 4096/vi.channels; + + /* OK, got and parsed all three headers. Initialize the Vorbis + packet->PCM decoder. */ + vorbis_synthesis_init (&vd, &vi); /* central decode state */ + vorbis_block_init (&vd, &vb); /* local state for most of the decode + so multiple block decodes can + proceed in parallel. We could init + multiple vorbis_block structures + for vd here */ + + /* The rest is just a straight decode loop until end of stream */ + while (!eos) { + while (!eos) { + int result = ogg_sync_pageout (&oy, &og); + if (result ==0) break; /* need more data */ + if (result ==-1) { /* missing or corrupt data at this page position */ + } else { + ogg_stream_pagein (&os, &og); /* can safely ignore errors at + this point */ + while (1) { + result=ogg_stream_packetout (&os, &op); + + if (result == 0) break; /* need more data */ + if (result == -1) { /* missing or corrupt data at this page position */ + /* no reason to complain; already complained above */ + } else { + /* we have a packet. Decode it */ + float **pcm; + int samples; + + if (vorbis_synthesis (&vb, &op) == 0) /* test for success! */ + vorbis_synthesis_blockin (&vd, &vb); + + + /* + + **pcm is a multichannel double vector. In stereo, for + example, pcm[0] is left, and pcm[1] is right. samples is + the size of each channel. Convert the float values + (-1.<=range<=1.) to whatever PCM format and write it out */ + + while ((samples = vorbis_synthesis_pcmout (&vd, &pcm)) > 0) { + int j; + int clipflag = 0; + int bout = (samples < vorbisdec->convsize ? samples : vorbisdec->convsize); + + outbuf = gst_buffer_new (); + GST_BUFFER_DATA (outbuf) = g_malloc (2*vi.channels * bout); + GST_BUFFER_SIZE (outbuf) = 2*vi.channels * bout; + + /* convert doubles to 16 bit signed ints (host order) and + interleave */ + for (i=0; i32767){ + val=32767; + clipflag=1; + } + if (val<-32768){ + val=-32768; + clipflag=1; + } + *ptr=val; + ptr+=vi.channels; + } + } + + if (need_sync) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLUSH); + need_sync = FALSE; + } + + GST_DEBUG (0,"vorbisdec: push\n"); + gst_pad_push (vorbisdec->srcpad, outbuf); + GST_DEBUG (0,"vorbisdec: push done\n"); + + vorbis_synthesis_read (&vd, bout); /* tell libvorbis how + many samples we + actually consumed */ + } + } + } + if (ogg_page_eos (&og)) eos=1; + } + } + if (!eos) { + + /* FIXME HACK! trap COTHREAD_STOPPING here */ + if (GST_ELEMENT_IS_COTHREAD_STOPPING (vorbisdec)) { + GST_DEBUG(0, "HACK HACK HACK, switching to cothread zero on COTHREAD_STOPPING\n"); + cothread_switch(cothread_current_main()); + } + + GST_DEBUG (0,"vorbisdec: pull\n"); + buf = gst_pad_pull (vorbisdec->sinkpad); + if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLUSH)) { + /*g_print("reset\n"); */ + ogg_sync_reset(&oy); + need_sync = TRUE; + } + GST_DEBUG (0,"vorbisdec: pull done\n"); + bytes = GST_BUFFER_SIZE (buf); + buffer = ogg_sync_buffer (&oy, bytes); + memcpy (buffer, GST_BUFFER_DATA (buf), bytes); + gst_buffer_unref (buf); + + ogg_sync_wrote (&oy, bytes); + if (bytes == 0) { + g_print("vorbisdec: eos reached\n"); + eos=1; + } + } + } + + /* clean up this logical bitstream; before exit we see if we're + followed by another [chained] */ + + ogg_stream_clear (&os); + + /* ogg_page and ogg_packet structs always point to storage in + libvorbis. They're never freed or manipulated directly */ + + vorbis_block_clear (&vb); + vorbis_dsp_clear (&vd); + vorbis_info_clear (&vi); /* must be called last */ + } + + /* OK, clean up the framer */ + ogg_sync_clear (&oy); + + g_print("vorbisdec: end\n"); +} + diff --git a/ext/vorbis/vorbisdec.h b/ext/vorbis/vorbisdec.h new file mode 100644 index 0000000000..96f3c42c75 --- /dev/null +++ b/ext/vorbis/vorbisdec.h @@ -0,0 +1,83 @@ +/* Gnome-Streamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __VORBISDEC_H__ +#define __VORBISDEC_H__ + + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_VORBISDEC \ + (vorbisdec_get_type()) +#define GST_VORBISDEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VORBISDEC,VorbisDec)) +#define GST_VORBISDEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VORBISDEC,VorbisDec)) +#define GST_IS_VORBISDEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VORBISDEC)) +#define GST_IS_VORBISDEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VORBISDEC)) + +typedef struct _VorbisDec VorbisDec; +typedef struct _VorbisDecClass VorbisDecClass; + +struct _VorbisDec { + GstElement element; + + GstPad *sinkpad,*srcpad; + + ogg_sync_state oy; /* sync and verify incoming physical bitstream */ + ogg_stream_state os; /* take physical pages, weld into a logical + stream of packets */ + ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */ + ogg_packet op; /* one raw packet of data for decode */ + + vorbis_info vi; /* struct that stores all the static vorbis bitstream + settings */ + vorbis_comment vc; /* struct that stores all the bitstream user comments */ + vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ + vorbis_block vb; /* local working space for packet->PCM decode */ + + gboolean eos; + guint state; + int16_t convsize; +}; + +struct _VorbisDecClass { + GstElementClass parent_class; +}; + +GType vorbisdec_get_type(void); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __VORBISDEC_H__ */ diff --git a/ext/vorbis/vorbisenc.c b/ext/vorbis/vorbisenc.c new file mode 100644 index 0000000000..30dbd942ac --- /dev/null +++ b/ext/vorbis/vorbisenc.c @@ -0,0 +1,309 @@ +/* Gnome-Streamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#include +#include + +#include + +#include "vorbisenc.h" + + + +extern GstPadTemplate *enc_src_template, *enc_sink_template; + +/* elementfactory information */ +GstElementDetails vorbisenc_details = { + "Ogg Vorbis encoder", + "Filter/Audio/Encoder", + "Encodes audio in OGG Vorbis format", + VERSION, + "Monty , "\ + "Wim Taymans ", + "(C) 2000", +}; + +/* VorbisEnc signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_BITRATE, +}; + +static void gst_vorbisenc_class_init (VorbisEncClass *klass); +static void gst_vorbisenc_init (VorbisEnc *vorbisenc); + +static void gst_vorbisenc_chain (GstPad *pad, GstBuffer *buf); +static void gst_vorbisenc_setup (VorbisEnc *vorbisenc); + +static void gst_vorbisenc_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static void gst_vorbisenc_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); + +static GstElementClass *parent_class = NULL; +/*static guint gst_vorbisenc_signals[LAST_SIGNAL] = { 0 }; */ + +GType +vorbisenc_get_type (void) +{ + static GType vorbisenc_type = 0; + + if (!vorbisenc_type) { + static const GTypeInfo vorbisenc_info = { + sizeof(VorbisEncClass), NULL, + NULL, + (GClassInitFunc)gst_vorbisenc_class_init, + NULL, + NULL, + sizeof(VorbisEnc), + 0, + (GInstanceInitFunc)gst_vorbisenc_init, + }; + vorbisenc_type = g_type_register_static(GST_TYPE_ELEMENT, "VorbisEnc", &vorbisenc_info, 0); + } + return vorbisenc_type; +} + +static void +gst_vorbisenc_class_init (VorbisEncClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass*)klass; + gstelement_class = (GstElementClass*)klass; + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BITRATE, + g_param_spec_int("bitrate","bitrate","bitrate", + G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); /* CHECKME */ + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); + + gobject_class->set_property = gst_vorbisenc_set_property; + gobject_class->get_property = gst_vorbisenc_get_property; +} + +static void +gst_vorbisenc_newcaps (GstPad *pad, GstCaps *caps) +{ + VorbisEnc *vorbisenc; + + vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad)); + + vorbisenc->channels = gst_caps_get_int (caps, "channels"); + vorbisenc->frequency = gst_caps_get_int (caps, "rate"); + + gst_vorbisenc_setup (vorbisenc); +} + +static void +gst_vorbisenc_init (VorbisEnc *vorbisenc) +{ + vorbisenc->sinkpad = gst_pad_new_from_template (enc_sink_template, "sink"); + gst_element_add_pad(GST_ELEMENT(vorbisenc),vorbisenc->sinkpad); + gst_pad_set_chain_function(vorbisenc->sinkpad,gst_vorbisenc_chain); + gst_pad_set_newcaps_function (vorbisenc->sinkpad, gst_vorbisenc_newcaps); + + vorbisenc->srcpad = gst_pad_new_from_template (enc_src_template, "src"); + gst_pad_set_caps (vorbisenc->srcpad, gst_pad_get_padtemplate_caps (vorbisenc->srcpad)); + gst_element_add_pad(GST_ELEMENT(vorbisenc),vorbisenc->srcpad); + + vorbisenc->channels = 2; + vorbisenc->frequency = 44100; + vorbisenc->bitrate = 128000; +} + +static void +gst_vorbisenc_setup (VorbisEnc *vorbisenc) +{ + /********** Encode setup ************/ + + /* choose an encoding mode */ + /* (mode 0: 44kHz stereo uncoupled, roughly 128kbps VBR) */ + vorbis_info_init(&vorbisenc->vi); + vorbis_encode_init(&vorbisenc->vi, vorbisenc->channels, vorbisenc->frequency, + -1, vorbisenc->bitrate, -1); + + /* add a comment */ + vorbis_comment_init(&vorbisenc->vc); + vorbis_comment_add(&vorbisenc->vc,"Track encoded by GStreamer:vorbisenc"); + + /* set up the analysis state and auxiliary encoding storage */ + vorbis_analysis_init(&vorbisenc->vd,&vorbisenc->vi); + vorbis_block_init(&vorbisenc->vd,&vorbisenc->vb); + + /* set up our packet->stream encoder */ + /* pick a random serial number; that way we can more likely build + chained streams just by concatenation */ + srand(time(NULL)); + ogg_stream_init(&vorbisenc->os,rand()); + + /* Vorbis streams begin with three headers; the initial header (with + most of the codec setup parameters) which is mandated by the Ogg + bitstream spec. The second header holds any comment fields. The + third header holds the bitstream codebook. We merely need to + make the headers, then pass them to libvorbis one at a time; + libvorbis handles the additional Ogg bitstream constraints */ + + { + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + + vorbis_analysis_headerout(&vorbisenc->vd,&vorbisenc->vc,&header,&header_comm,&header_code); + ogg_stream_packetin(&vorbisenc->os,&header); /* automatically placed in its own + page */ + ogg_stream_packetin(&vorbisenc->os,&header_comm); + ogg_stream_packetin(&vorbisenc->os,&header_code); + + /* no need to write out here. We'll get to that in the main loop */ + } +} + +static void +gst_vorbisenc_chain (GstPad *pad,GstBuffer *buf) +{ + VorbisEnc *vorbisenc; + gint16 *data; + gulong size; + gulong i, j; + GstBuffer *outbuf; + + g_return_if_fail(pad != NULL); + g_return_if_fail(GST_IS_PAD(pad)); + g_return_if_fail(buf != NULL); + + vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad)); + data = (gint16 *)GST_BUFFER_DATA(buf); + size = GST_BUFFER_SIZE(buf)/2; + + if(size==0){ + /* end of file. this can be done implicitly in the mainline, + but it's easier to see here in non-clever fashion. + Tell the library we're at end of stream so that it can handle + the last frame and mark end of stream in the output properly */ + vorbis_analysis_wrote(&vorbisenc->vd,0); + + }else{ + /* data to encode */ + + /* expose the buffer to submit data */ + float **buffer=vorbis_analysis_buffer(&vorbisenc->vd,size/vorbisenc->channels); + + /* uninterleave samples */ + for(i=0;ichannels;i++) + { + for(j=0;jchannels;j++) + buffer[j][i] = data[i*vorbisenc->channels+j]/32768.f; + } + + /* tell the library how much we actually submitted */ + vorbis_analysis_wrote(&vorbisenc->vd, size/vorbisenc->channels); + } + + /* vorbis does some data preanalysis, then divvies up blocks for + more involved (potentially parallel) processing. Get a single + block for encoding now */ + while(vorbis_analysis_blockout(&vorbisenc->vd,&vorbisenc->vb)==1){ + + /* analysis */ + vorbis_analysis(&vorbisenc->vb,&vorbisenc->op); + + /* weld the packet into the bitstream */ + ogg_stream_packetin(&vorbisenc->os,&vorbisenc->op); + + /* write out pages (if any) */ + while(!vorbisenc->eos){ + int result=ogg_stream_pageout(&vorbisenc->os,&vorbisenc->og); + if(result==0)break; + + outbuf = gst_buffer_new(); + GST_BUFFER_DATA(outbuf) = g_malloc(vorbisenc->og.header_len+vorbisenc->og.body_len); + GST_BUFFER_SIZE(outbuf) = vorbisenc->og.header_len+vorbisenc->og.body_len; + + memcpy(GST_BUFFER_DATA(outbuf), vorbisenc->og.header, vorbisenc->og.header_len); + memcpy(GST_BUFFER_DATA(outbuf)+vorbisenc->og.header_len, vorbisenc->og.body, vorbisenc->og.body_len); + + GST_DEBUG (0, "vorbisenc: encoded buffer of %d bytes\n", GST_BUFFER_SIZE(outbuf)); + + gst_pad_push(vorbisenc->srcpad, outbuf); + + /* this could be set above, but for illustrative purposes, I do + it here (to show that vorbis does know where the stream ends) */ + + if(ogg_page_eos(&vorbisenc->og)) { + vorbisenc->eos=1; + } + } + } + + if (vorbisenc->eos) { + /* clean up and exit. vorbis_info_clear() must be called last */ + + ogg_stream_clear(&vorbisenc->os); + vorbis_block_clear(&vorbisenc->vb); + vorbis_dsp_clear(&vorbisenc->vd); + vorbis_info_clear(&vorbisenc->vi); + } + + gst_buffer_unref(buf); +} + +static void +gst_vorbisenc_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + VorbisEnc *vorbisenc; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_VORBISENC (object)); + + vorbisenc = GST_VORBISENC (object); + + switch (prop_id) { + case ARG_BITRATE: + g_value_set_int (value, vorbisenc->bitrate); + break; + default: + break; + } +} + +static void +gst_vorbisenc_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + VorbisEnc *vorbisenc; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_VORBISENC (object)); + + vorbisenc = GST_VORBISENC (object); + + switch (prop_id) { + case ARG_BITRATE: + vorbisenc->bitrate = g_value_get_int (value); + break; + default: + break; + } +} + diff --git a/ext/vorbis/vorbisenc.h b/ext/vorbis/vorbisenc.h new file mode 100644 index 0000000000..dd647f47c0 --- /dev/null +++ b/ext/vorbis/vorbisenc.h @@ -0,0 +1,84 @@ +/* Gnome-Streamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __VORBISENC_H__ +#define __VORBISENC_H__ + + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GST_TYPE_VORBISENC \ + (vorbisenc_get_type()) +#define GST_VORBISENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VORBISENC,VorbisEnc)) +#define GST_VORBISENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_VORBISENC,VorbisEnc)) +#define GST_IS_VORBISENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_VORBISENC)) +#define GST_IS_VORBISENC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_VORBISENC)) + +typedef struct _VorbisEnc VorbisEnc; +typedef struct _VorbisEncClass VorbisEncClass; + +struct _VorbisEnc { + GstElement element; + + GstPad *sinkpad,*srcpad; + + ogg_stream_state os; /* take physical pages, weld into a logical + stream of packets */ + ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */ + ogg_packet op; /* one raw packet of data for decode */ + + vorbis_info vi; /* struct that stores all the static vorbis bitstream + settings */ + vorbis_comment vc; /* struct that stores all the user comments */ + + vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ + vorbis_block vb; /* local working space for packet->PCM decode */ + + gboolean eos; + + gint bitrate; + gint channels; + gint frequency; +}; + +struct _VorbisEncClass { + GstElementClass parent_class; +}; + +GType vorbisenc_get_type(void); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __VORBISENC_H__ */