www.pudn.com > simplec++codeforp2p.rar > ogg.cpp


// ------------------------------------------------ 
// File : mp3.cpp 
// Date: 28-may-2003 
// Author: giles 
// 
// (c) 2002-3 peercast.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. 
// ------------------------------------------------ 
 
#include "channel.h" 
#include "ogg.h" 
 
 
static int test=0; 
// ------------------------------------------ 
void OGGStream::readHeader(Stream &,Channel *) 
{ 
	test = 0; 
} 
 
// ------------------------------------------ 
void OGGStream::readEnd(Stream &,Channel *) 
{ 
} 
 
// ------------------------------------------ 
int OGGStream::readPacket(Stream &in,Channel *ch) 
{ 
	OggPage ogg; 
	ChanPacket pack; 
 
	ogg.read(in); 
 
	if (ogg.isBOS()) 
	{ 
		if (!vorbis.needHeader() && !theora.needHeader()) 
		{ 
			ch->headPack.len = 0; 
		} 
 
		if (ogg.detectVorbis()) 
			vorbis.bos(ogg.getSerialNo()); 
		if (ogg.detectTheora()) 
			theora.bos(ogg.getSerialNo()); 
	} 
 
	if (ogg.isEOS()) 
	{ 
 
		if (ogg.getSerialNo() == vorbis.serialNo) 
		{ 
			LOG_CHANNEL("Vorbis stream: EOS"); 
			vorbis.eos(); 
		} 
		if (ogg.getSerialNo() == theora.serialNo) 
		{ 
			LOG_CHANNEL("Theora stream: EOS"); 
			theora.eos(); 
		} 
	} 
 
	if (vorbis.needHeader() || theora.needHeader()) 
	{ 
 
		if (ogg.getSerialNo() == vorbis.serialNo) 
			vorbis.readHeader(ch,ogg); 
		else if (ogg.getSerialNo() == theora.serialNo) 
			theora.readHeader(ch,ogg); 
		else 
			throw StreamException("Bad OGG serial no."); 
 
 
		if (!vorbis.needHeader() && !theora.needHeader()) 
		{ 
 
			ch->info.bitrate = 0; 
 
			if (vorbis.isActive()) 
				ch->info.bitrate += vorbis.bitrate; 
 
			if (theora.isActive()) 
			{ 
				ch->info.bitrate += theora.bitrate; 
				ch->info.contentType = ChanInfo::T_OGM; 
			} 
 
			 
			ch->headPack.type = ChanPacket::T_HEAD; 
			ch->headPack.pos = ch->streamPos; 
 
			ch->startTime = sys->getDTime();		 
 
			ch->streamPos += ch->headPack.len; 
 
			ch->newPacket(ch->headPack); 
			LOG_CHANNEL("Got %d bytes of headers",ch->headPack.len); 
		} 
 
	}else 
	{ 
 
 
		pack.init(ChanPacket::T_DATA,ogg.data,ogg.headLen+ogg.bodyLen,ch->streamPos); 
		ch->newPacket(pack); 
 
		ch->streamPos+=pack.len; 
 
		if (theora.isActive()) 
		{ 
			if (ogg.getSerialNo() == theora.serialNo) 
			{ 
				ch->sleepUntil(theora.getTime(ogg)); 
			} 
		}else if (vorbis.isActive()) 
		{ 
			if (ogg.getSerialNo() == vorbis.serialNo) 
			{ 
				ch->sleepUntil(vorbis.getTime(ogg)); 
			} 
		} 
 
	} 
	return 0; 
} 
 
// ----------------------------------- 
void OggSubStream::readHeader(Channel *ch,OggPage &ogg) 
{ 
	if ((pack.bodyLen + ogg.bodyLen) >= OggPacket::MAX_BODYLEN) 
		throw StreamException("OGG packet too big"); 
 
	if (ch->headPack.len+(ogg.bodyLen+ogg.headLen) >= ChanMeta::MAX_DATALEN) 
		throw StreamException("OGG packet too big for headMeta"); 
 
// copy complete packet into head packet 
	memcpy(&ch->headPack.data[ch->headPack.len],ogg.data,ogg.headLen+ogg.bodyLen); 
	ch->headPack.len += ogg.headLen+ogg.bodyLen; 
 
	// add body to packet 
	memcpy(&pack.body[pack.bodyLen],&ogg.data[ogg.headLen],ogg.bodyLen); 
	pack.bodyLen += ogg.bodyLen; 
 
	pack.addLacing(ogg); 
 
	if (pack.numPackets >= maxHeaders) 
		procHeaders(ch); 
 
} 
// ----------------------------------- 
void OggVorbisSubStream::procHeaders(Channel *ch) 
{ 
	unsigned int packPtr=0; 
 
	for(int i=0; iinfo); 
				break; 
			case 3: // comment 
				{ 
					LOG_CHANNEL("OGG Vorbis Header: Comment (%d bytes)",vin.len); 
					ChanInfo newInfo = ch->info; 
					readComment(vin,newInfo); 
					ch->updateInfo(newInfo); 
				} 
				break; 
			case 5: // setup 
				LOG_CHANNEL("OGG Vorbis Header: Setup (%d bytes)",vin.len); 
				//readSetup(vin); 
				break; 
			default: 
				throw StreamException("Unknown Vorbis packet header type"); 
				break; 
		} 
	} 
 
} 
// ----------------------------------- 
double OggTheoraSubStream::getTime(OggPage &ogg) 
{ 
    int64_t iframe=ogg.granPos>>granposShift; 
    int64_t pframe=ogg.granPos-(iframe<info); 
				break; 
			default: 
				LOG_CHANNEL("OGG Theora Header: Unknown %d (%d bytes)",id[0] & 0xff,vin.len); 
				break; 
		} 
 
 
 
	} 
 
} 
 
// ----------------------------------- 
double OggVorbisSubStream::getTime(OggPage &ogg) 
{ 
	return (double)ogg.granPos / (double)samplerate; 
 
} 
 
// ----------------------------------- 
void OggVorbisSubStream::readIdent(Stream &in, ChanInfo &info) 
{ 
	int ver = in.readLong(); 
	int chans = in.readChar(); 
	samplerate = in.readLong(); 
	int brMax = in.readLong(); 
	int brNom = in.readLong(); 
	int brLow = in.readLong(); 
	 
 
	in.readChar();	// skip blocksize 0+1 
 
	LOG_CHANNEL("OGG Vorbis Ident: ver=%d, chans=%d, rate=%d, brMax=%d, brNom=%d, brLow=%d", 
		ver,chans,samplerate,brMax,brNom,brLow); 
 
 
	bitrate = brNom/1000; 
 
	char frame = in.readChar();		// framing bit 
	if (!frame) 
		throw StreamException("Bad Indent frame"); 
 
} 
 
  
// ----------------------------------- 
void OggVorbisSubStream::readSetup(Stream &in) 
{ 
	// skip everything in packet 
	int cnt=0; 
	while (!in.eof()) 
	{ 
		cnt++; 
		in.readChar(); 
	} 
 
	LOG_CHANNEL("Read %d bytes of Vorbis Setup",cnt); 
} 
// ----------------------------------- 
void OggVorbisSubStream::readComment(Stream &in, ChanInfo &info) 
{ 
	int vLen = in.readLong();	// vendor len 
 
	in.skip(vLen); 
 
	char argBuf[8192]; 
 
	info.track.clear(); 
 
	int cLen = in.readLong();	// comment len 
	for(int i=0; i sizeof(argBuf)) 
			throw StreamException("Comment string too long"); 
		in.read(argBuf,l); 
		argBuf[l] = 0; 
		LOG_CHANNEL("OGG Comment: %s",argBuf); 
 
		char *arg; 
		if ((arg=stristr(argBuf,"ARTIST="))) 
		{ 
			info.track.artist.set(arg+7,String::T_ASCII); 
			info.track.artist.convertTo(String::T_UNICODE); 
 
		}else if ((arg=stristr(argBuf,"TITLE="))) 
		{ 
			info.track.title.set(arg+6,String::T_ASCII); 
			info.track.title.convertTo(String::T_UNICODE); 
 
		}else if ((arg=stristr(argBuf,"GENRE="))) 
		{ 
			info.track.genre.set(arg+6,String::T_ASCII); 
			info.track.genre.convertTo(String::T_UNICODE); 
 
		}else if ((arg=stristr(argBuf,"CONTACT="))) 
		{ 
			info.track.contact.set(arg+8,String::T_ASCII); 
			info.track.contact.convertTo(String::T_UNICODE); 
 
		}else if ((arg=stristr(argBuf,"ALBUM="))) 
		{ 
			info.track.album.set(arg+6,String::T_ASCII); 
			info.track.album.convertTo(String::T_UNICODE); 
		} 
	} 
 
	char frame = in.readChar();		// framing bit 
	if (!frame) 
		throw StreamException("Bad Comment frame"); 
 
//	updateMeta(); 
 
} 
 
// ----------------------------------- 
bool OggPage::isBOS() 
{ 
	return (data[5] & 0x02) != 0; 
} 
// ----------------------------------- 
bool OggPage::isEOS() 
{ 
	return (data[5] & 0x04) != 0; 
} 
// ----------------------------------- 
bool OggPage::isNewPacket() 
{ 
	return (data[5] & 0x01) == 0; 
} 
// ----------------------------------- 
bool OggPage::isHeader() 
{ 
	return ((*(unsigned int *)&data[6]) || (*(unsigned int *)&data[10])) == 0; 
} 
// ----------------------------------- 
unsigned int OggPage::getSerialNo() 
{ 
	return *(unsigned int *)&data[14]; 
} 
 
// ----------------------------------- 
void OggPage::read(Stream &in) 
{ 
	// skip until we get OGG capture pattern 
	bool gotOgg=false; 
	while (!gotOgg) 
	{ 
		if (in.readChar() == 'O') 
			if (in.readChar() == 'g') 
				if (in.readChar() == 'g') 
					if (in.readChar() == 'S') 
						gotOgg = true; 
		if (!gotOgg) 
			LOG_CHANNEL("Skipping OGG packet"); 
	} 
 
 
 
	memcpy(&data[0],"OggS",4); 
 
	in.read(&data[4],27-4); 
 
	int numSegs = data[26]; 
	bodyLen = 0; 
 
	// read segment table 
	in.read(&data[27],numSegs); 
	for(int i=0; i= MAX_BODYLEN)		 
		throw StreamException("OGG body too big"); 
 
	headLen = 27+numSegs; 
 
	if (headLen > MAX_HEADERLEN) 
		throw StreamException("OGG header too big"); 
 
	in.read(&data[headLen],bodyLen); 
 
	granPos = *(unsigned int *)&data[10]; 
	granPos <<= 32; 
	granPos |= *(unsigned int *)&data[6]; 
 
 
	#if 0 
		LOG_DEBUG("OGG Packet - page %d, id = %x - %s %s %s - %d:%d - %d segs, %d bytes", 
			*(unsigned int *)&data[18], 
			*(unsigned int *)&data[14], 
			data[5]&0x1?"cont":"new", 
			data[5]&0x2?"bos":"", 
			data[5]&0x4?"eos":"", 
			(unsigned int)(granPos>>32), 
			(unsigned int)(granPos&0xffffffff), 
			numSegs, 
			headLen+bodyLen); 
 
	#endif 
 
} 
// ----------------------------------- 
bool OggPage::detectVorbis() 
{ 
	return memcmp(&data[headLen+1],"vorbis",6) == 0; 
} 
// ----------------------------------- 
bool OggPage::detectTheora() 
{ 
	return memcmp(&data[headLen+1],"theora",6) == 0; 
} 
 
// ----------------------------------- 
void	OggPacket::addLacing(OggPage &ogg) 
{ 
	 
	int numSegs = ogg.data[26]; 
	for(int i=0; i= MAX_PACKETS) 
				throw StreamException("Too many OGG packets"); 
			packetSizes[numPackets]=0; 
		} 
	} 
 
}