EVOLUTION-MANAGER
Edit File: oggStreamEncoder.cpp
/* * oggStreamEncoder is a class to insert an ogg packet into an ogg page stream * * Copyright (C) 2008 Joern Seger * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include <iostream> #include <cstring> #include <cstdlib> #include "definition.h" #include "oggStreamEncoder.h" #include "oggHeader.h" #include "crc.h" #define min(a,b) ((a<b)?(a):(b)) #define max(a,b) ((a>b)?(a):(b)) std::vector<uint32> OggStreamEncoder::usedSerialNo; OggStreamEncoder::OggStreamEncoder(uint32 serial) : maxPageSize(4096), streamNo(0), dataLength(0), dataSegments(0), usedData(0), pageCounter(0) // packetCounter(0), positionInterpreterEnabled(false), pageKeepEnabled(false), //posInterpreter(0) { uint32 newSerial = findUniqueSerial(serial); /* if (newSerial != serial) std::cerr << "OggStreamEncoder::OggStreamEncoder: serial number <" << std::hex << serial << "> is in use - using <" << newSerial << std::dec << "> instead\n"; */ streamSerialNo = newSerial; setInitialized(); } OggStreamEncoder::~OggStreamEncoder() { if (!oggPacketList.empty()) std::cerr << "OggStreamEncoder::Destructor: WARNING packet list not empty\n"; if (!oggPageList.empty()) std::cerr << "OggStreamEncoder::Destructor: WARNING page list not empty\n"; } // we need a global repository to keep an Eye on the serial numbers uint32 OggStreamEncoder::findUniqueSerial(uint32 origSerial) { bool isUnique(false); uint32 serial; while (!isUnique) { serial = (origSerial?origSerial:rand()); isUnique = true; origSerial = 0; for (uint32 i(0); i<usedSerialNo.size(); ++i) if (serial == usedSerialNo[i]) isUnique = false; } usedSerialNo.push_back(serial); return(serial); } /* void OggStreamEncoder::enablePositionInterpreter() { positionInterpreterEnabled = true; } void OggStreamEncoder::keepOnePage() { pageKeepEnabled = true; } */ void OggStreamEncoder::addPacket(OggPacket& packet) { /* if we want to interprete the position by ourself */ /* if (positionInterpreterEnabled && posInterpreter && (!packet.isStreamHeader())) posInterpreter->setStreamPosition(packet); */ /* This is a normal packet * Let's start to calculate the actual length */ oggPacketList.push_back(packet); dataLength += packet.length(); uint32 actSegmentsSize = (packet.length()+255)/255; uint8 actSegments[maxSegmentEntries]; if (actSegmentsSize > maxSegmentEntries) throw "OggStreamEncoder::addPacket: Not able to handle this packet size"; /* calculate the segment table part of this packet */ memset(actSegments, 0xff, actSegmentsSize-1); actSegments[actSegmentsSize-1] = packet.length()%255; segmentsBuffer.addData(actSegments, actSegmentsSize); } bool OggStreamEncoder::getNextPacketLength(uint32 pageBorder, uint32& length, uint32& segments) { /* initialize the values */ length = 0; segments = 0; /* if the data length is in range, do nothing */ if ((dataLength < pageBorder) && (segmentsBuffer.getUsed() < 255)) { return(false); } /* get the actually available segments (maximum 255)*/ uint8 actSegments[255]; uint32 actSegmentsSize = segmentsBuffer.luenkerfront(actSegments,255); /* and calculate, how many segments we want to include into the * actual page */ for (uint32 count(0); count<actSegmentsSize; ++count) { length += actSegments[count]; segments++; if ((length >= pageBorder) || (segments > 254)) return(true); } return(true); } OggStreamEncoder& OggStreamEncoder::operator<<(OggPacket packet) { if (!isInitialized()) { std::cerr << "OggStreamEncoder::operator<<: Stream is not initialized correctly\n"; return(*this); } if (!isConfigured()) { // this must be the bos packet if (!packet.isBOS()) { std::cerr << "OggStreamEncoder::operator<<: First packet must be a BOS packet\n"; return(*this); } else { /* THIS IS NOT CLEAN!!!!!!!*/ // ExtractorInformation info; // oggBOSExtractorFactory::extractInformation(packet) // posInterpreter = oggBOSExtractorFactory::extractPositionInterpreter(info); streamNo = packet.getStreamNo(); } /* add the packet to the temporal buffer */ addPacket(packet); /* the encoder stream is configured */ setConfigured(); /* flush the first packet (see Spec) */ flush(); return(*this); } /* add the packet to the temporal buffer */ addPacket(packet); /* create as many packets as data is available */ while (dataLength > maxPageSize) { createPage(maxPageSize); } return(*this); } OggStreamEncoder& OggStreamEncoder::operator>>(OggPage& page) { if (isEmpty()) { std::cerr << "OggStreamEncoder::opertator>>: no page available\n"; return(*this); } page = oggPageList.front(); oggPageList.pop_front(); page.obj()->streamNo = streamNo; // std::cerr << "OggStreamEncoder::opertator>>: page output\n"; // page.print(); /* if (pageKeepEnabled) { if (oggPageList.size() < 2) setEmpty(); } else { */ if (oggPageList.empty()) setEmpty(); return(*this); } void OggStreamEncoder::flush() { while (dataLength) createPage(dataLength); } void OggStreamEncoder::createPage(uint32 minPageSize) { uint32 bodyLength; uint32 segmentsSize; /* Is there enough data available? */ if (!getNextPacketLength(minPageSize, bodyLength, segmentsSize)) return; uint32 overallLength = sizeof(OggHeader) + segmentsSize + bodyLength; uint8* pageData = new uint8[overallLength]; /* an ogg page looks like this: * -------------------------------------------------------- * | Ogg Header | Segments Table | Packet1 | Packet2 | ... * -------------------------------------------------------- */ OggHeader* header ((OggHeader*)pageData); uint8* segments (pageData+sizeof(OggHeader)); uint8* body (segments+segmentsSize); /* set the header information */ #ifdef HAVE_BZERO bzero(header,sizeof(OggHeader)); #else for (uint32 i(0); i<sizeof(OggHeader); ++i) *(((uint8*)header)+i) = 0; #endif memcpy(header->ogg,"OggS",4); header->tableSegments = segmentsSize; header->pageNo = pageCounter++; header->serial = streamSerialNo; header->position = -1; if (usedData) header->pack_type = 1; // is packet starts on the previous page /* fill the segments */ segmentsBuffer.getData(segments,segmentsSize); std::list<OggPacket>::iterator it(oggPacketList.begin()); /* if this is a bos packet, this would surely be the first packet */ if (it->isBOS()) header->page_type = 1; uint32 arrayIndex(0); for (; it != oggPacketList.end(); it++) { uint32 cpyLength = min((it->length()-usedData),(bodyLength-arrayIndex)); memcpy(body+arrayIndex, it->data()+usedData, cpyLength); arrayIndex += cpyLength; /* is this the end of the actual page */ if (arrayIndex == bodyLength) { /* the packet does not start within this page and does not end * on this page? */ if (usedData != 0) { usedData += cpyLength; if (usedData == it->length()) { usedData = 0; if (it->isEOS()) header->last = 1; /* the packet is fully used, so point to the next valid packet */ ++it; // this might be the .end() sign, however, how cares } } else { if (cpyLength == it->length()) { /* this packet ended on this page */ usedData = 0; /* at lease the actual Packet has been completed on this page */ // onePacketCompleted = true; /* Is it the end of a stream? Then mark it as such */ if (it->isEOS()) header->last = 1; /* the packet is fully used, so point to the next valid packet */ ++it; // this might be the .end() sign, however, how cares } else { /* keep the number of copied bytes for the next page */ usedData = cpyLength; } } // we found the end of the page break; } usedData = 0; } /* set the granule position if at least one packet has ended on this page * The position is taken from the last full packet */ if (oggPacketList.begin() != it) { std::list<OggPacket>::iterator it1 = it; it1--; header->position = it1->granulepos(); oggPacketList.erase(oggPacketList.begin(), it); } header->checksum = Crc::create(pageData, overallLength); OggPage page(new OggPageInternal(pageData, sizeof(OggHeader)+segmentsSize, bodyLength)); oggPageList.push_back(page); dataLength -= bodyLength; setAvailable(); } /* OggPage OggStreamEncoder::getLastPage() { OggPage page; if (oggPageList.size() == 0) { std::cerr << "OggStreamEncoder::getLastPage: no last page available\n"; return(page); } if (oggPageList.size() > 1) { std::cerr << "OggStreamEncoder::getLastPage: more than one packet available\n"; return(page); } page = oggPageList.front(); oggPageList.pop_front(); // write end of stream OggHeader* header = (OggHeader*)(page.data()); header->last = 1; header->checksum = 0; header->checksum = Crc::create(page.data(), page.length()); return (page); } */