diff --git a/client/Makefile b/client/Makefile index bc121a91..03cb36ec 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,9 +1,9 @@ VERSION = 0.01 CC = /usr/bin/g++ CFLAGS = -std=gnu++0x -Wall -Wno-unused-function -O3 -D_REENTRANT -DVERSION=\"$(VERSION)\" -I.. -LDFLAGS = -lrt -lpthread -lboost_system -lboost_program_options -lasound +LDFLAGS = -lrt -lpthread -lboost_system -lboost_program_options -lasound -logg -lvorbis -OBJ = snapClient.o stream.o player.o receiver.o ../common/chunk.o ../common/log.o ../common/sampleFormat.o +OBJ = snapClient.o stream.o player.o receiver.o pcmDecoder.o oggDecoder.o ../common/chunk.o ../common/log.o ../common/sampleFormat.o BIN = snapclient all: client diff --git a/client/Makefile~ b/client/Makefile~ new file mode 100644 index 00000000..29d7d00a --- /dev/null +++ b/client/Makefile~ @@ -0,0 +1,18 @@ +VERSION = 0.01 +CC = /usr/bin/g++ +CFLAGS = -std=gnu++0x -Wall -Wno-unused-function -O3 -D_REENTRANT -DVERSION=\"$(VERSION)\" -I.. +LDFLAGS = -lrt -lpthread -lportaudio -lboost_system -lboost_program_options + +OBJ = snapClient.o stream.o ../common/chunk.o ../common/log.o +BIN = snapclient + +all: client + +client: $(OBJ) + $(CC) $(CFLAGS) -o $(BIN) $(OBJ) $(LDFLAGS) + +%.o: %.cpp + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -rf $(BIN) $(OBJ) diff --git a/client/decoder.h b/client/decoder.h new file mode 100644 index 00000000..4c797880 --- /dev/null +++ b/client/decoder.h @@ -0,0 +1,16 @@ +#ifndef DECODER_H +#define DECODER_H +#include "common/chunk.h" + + +class Decoder +{ +public: + Decoder(); + virtual bool decode(Chunk* chunk) = 0; +}; + + +#endif + + diff --git a/client/oggDecoder.cpp b/client/oggDecoder.cpp new file mode 100644 index 00000000..b9b3bb21 --- /dev/null +++ b/client/oggDecoder.cpp @@ -0,0 +1,246 @@ +#include "oggDecoder.h" +#include +#include +#include + +using namespace std; + + +OggDecoder::OggDecoder() +{ + init(); +} + + +bool OggDecoder::decode(Chunk* chunk) +{ + WireChunk* wireChunk = chunk->wireChunk; + int eos=0; + int i; + + /* 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. */ + + buffer=ogg_sync_buffer(&oy,wireChunk->length); + memcpy(buffer, wireChunk->payload, wireChunk->length); + bytes = wireChunk->length; + ogg_sync_wrote(&oy,bytes); + +if (first) +{ +cout << "1" << endl; + + if(ogg_sync_pageout(&oy,&og)!=1){ + fprintf(stderr,"Input does not appear to be an Ogg bitstream.\n"); + exit(1); + } + + ogg_stream_init(&os,ogg_page_serialno(&og)); +cout << "2" << endl; + + vorbis_info_init(&vi); + vorbis_comment_init(&vc); + if(ogg_stream_pagein(&os,&og)<0){ + fprintf(stderr,"Error reading first page of Ogg bitstream data.\n"); + exit(1); + } + + if(ogg_stream_packetout(&os,&op)!=1){ + fprintf(stderr,"Error reading initial header packet.\n"); + exit(1); + } + + if(vorbis_synthesis_headerin(&vi,&vc,&op)<0){ + fprintf(stderr,"This Ogg bitstream does not contain Vorbis " + "audio data.\n"); + exit(1); + } +cout << "3" << endl; + + + i=0; + while(i<2){ + while(i<2){ + int result=ogg_sync_pageout(&oy,&og); +cout << result << endl; + 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){ +cout << "a" << endl; + ogg_stream_pagein(&os,&og); /* we can ignore any errors here + as they'll also become apparent + at packetout */ + while(i<2){ +cout << "b" << endl; + result=ogg_stream_packetout(&os,&op); + if(result==0)break; + if(result<0){ + /* Uh oh; data at some point was corrupted or missing! + We can't tolerate that in a header. Die. */ + fprintf(stderr,"Corrupt secondary header. Exiting.\n"); + exit(1); + } + result=vorbis_synthesis_headerin(&vi,&vc,&op); + if(result<0){ + fprintf(stderr,"Corrupt secondary header. Exiting.\n"); + exit(1); + } + i++; + } + } + } + /* no harm in not checking before adding more */ +// buffer=ogg_sync_buffer(&oy,wireChunk->length); +// bytes=fread(buffer,1,4096,stdin); +// if(bytes==0 && i<2){ +// fprintf(stderr,"End of file before finding all Vorbis headers!\n"); +// 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){ + fprintf(stderr,"%s\n",*ptr); + ++ptr; + } + fprintf(stderr,"\nBitstream is %d channel, %ldHz\n",vi.channels,vi.rate); + fprintf(stderr,"Encoded by: %s\n\n",vc.vendor); + } + + + /* OK, got and parsed all three headers. Initialize the Vorbis + packet->PCM decoder. */ + if(vorbis_synthesis_init(&vd,&vi)==0){ /* 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 */ + } +first = false; +} + convsize=wireChunk->length/vi.channels; + /* 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<0){ /* missing or corrupt data at this page position */ + fprintf(stderr,"Corrupt or missing data in bitstream; " + "continuing...\n"); + }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<0){ /* 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 float 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 */ + + wireChunk->length = 0; + while((samples=vorbis_synthesis_pcmout(&vd,&pcm))>0){ + int j; + int clipflag=0; + int bout=(samples32767){ + val=32767; + clipflag=1; + } + if(val<-32768){ + val=-32768; + clipflag=1; + } + *ptr=val; + ptr+=vi.channels; + } + } + + if(clipflag) + fprintf(stderr,"Clipping in frame %ld\n",(long)(vd.sequence)); +//cout << "a" << endl; +size_t oldSize = wireChunk->length; +size_t size = 2*vi.channels * bout; + wireChunk->length += size; + wireChunk->payload = (char*)realloc(wireChunk->payload, wireChunk->length); + memcpy(wireChunk->payload + oldSize, convbuffer, size); +// fwrite(convbuffer,2*vi.channels,bout,stdout); + + vorbis_synthesis_read(&vd,bout); /* tell libvorbis how + many samples we + actually consumed */ + } + } + } +eos = 1; +// if(ogg_page_eos(&og))eos=1; + } + } +// if(!eos){ +// buffer=ogg_sync_buffer(&oy,4096); +// bytes=fread(buffer,1,4096,stdin); +// ogg_sync_wrote(&oy,bytes); +// if(bytes==0)eos=1; +// } + } + + /* 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); +// }else{ +// fprintf(stderr,"Error: Corrupt header during playback initialization.\n"); +// } + + /* clean up this logical bitstream; before exit we see if we're + followed by another [chained] */ + +// ogg_stream_clear(&os); +// vorbis_comment_clear(&vc); +// vorbis_info_clear(&vi); /* must be called last */ + return true; +} + + +void OggDecoder::init() +{ + ogg_sync_init(&oy); /* Now we can read pages */ +first = true; +} + + + diff --git a/client/oggDecoder.h b/client/oggDecoder.h new file mode 100644 index 00000000..363db79d --- /dev/null +++ b/client/oggDecoder.h @@ -0,0 +1,38 @@ +#ifndef OGG_DECODER_H +#define OGG_DECODER_H +#include "decoder.h" +#include + + +class OggDecoder +{ +public: + OggDecoder(); + virtual bool decode(Chunk* chunk); + +private: + void init(); + + 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 */ + ogg_int16_t convbuffer[4096]; /* take 8k out of the data segment, not the stack */ + int convsize=4096; + +bool first; + char *buffer; + int bytes; +}; + + +#endif + + diff --git a/client/pcmDecoder.cpp b/client/pcmDecoder.cpp new file mode 100644 index 00000000..8bfd3d1a --- /dev/null +++ b/client/pcmDecoder.cpp @@ -0,0 +1,19 @@ +#include "pcmDecoder.h" + +PcmDecoder::PcmDecoder() +{ +} + + +bool PcmDecoder::decode(Chunk* chunk) +{ +/* WireChunk* wireChunk = chunk->wireChunk; + for (size_t n=0; nlength; ++n) + wireChunk->payload[n] *= 1; +*/ + return true; +} + + + + diff --git a/client/pcmDecoder.h b/client/pcmDecoder.h new file mode 100644 index 00000000..5a04bae8 --- /dev/null +++ b/client/pcmDecoder.h @@ -0,0 +1,16 @@ +#ifndef PCM_DECODER_H +#define PCM_DECODER_H +#include "decoder.h" + + +class PcmDecoder +{ +public: + PcmDecoder(); + virtual bool decode(Chunk* chunk); +}; + + +#endif + + diff --git a/client/receiver.cpp b/client/receiver.cpp index 95c70543..2acb2259 100644 --- a/client/receiver.cpp +++ b/client/receiver.cpp @@ -2,6 +2,8 @@ #include #include #include "common/log.h" +#include "oggDecoder.h" +#include "pcmDecoder.h" #define PCM_DEVICE "default" @@ -45,6 +47,7 @@ void Receiver::stop() void Receiver::worker() { active_ = true; + PcmDecoder decoder; while (active_) { try @@ -64,8 +67,9 @@ void Receiver::worker() //cout << "WireChunk length: " << wireChunk->length << ", sec: " << wireChunk->tv_sec << ", usec: " << wireChunk->tv_usec << "\n"; wireChunk->payload = (char*)malloc(wireChunk->length); socketRead(&s, wireChunk->payload, wireChunk->length); - - stream_->addChunk(new Chunk(stream_->format, wireChunk)); + Chunk* chunk = new Chunk(stream_->format, wireChunk); + if (decoder.decode(chunk)) + stream_->addChunk(chunk); } } catch (const std::exception& e)