EVOLUTION-MANAGER
Edit File: theoraEncoder.cpp
/* * TheoraEncoder wrapper * * 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 "theoraEncoder.h" #ifdef HAVE_LIBTHEORADEC #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <iostream> #include <cstdlib> #include <cstring> TheoraEncoder::TheoraEncoder(uint32 _streamNo) : streamNo(_streamNo) { } TheoraEncoder::~TheoraEncoder() { if (isConfigured()) th_encode_free(theoraState); th_info_clear(&theoraInfo); // the original packet is owned by the encoder, so we are not allowed to delete it packet.packet = 0; } void TheoraEncoder::createHeader(std::vector<OggPacket>& headerList, std::vector<OggComment>& oggComments) { th_comment theoraComment; int32 encodeRetID; th_comment_init(&theoraComment); th_comment_add_tag(&theoraComment,"ENCODER",PACKAGE_STRING); /* add other comments */ for (uint32 i(0); i<oggComments.size(); ++i) th_comment_add_tag(&theoraComment, (char*) oggComments[i].tag.c_str(), (char*) oggComments[i].value.c_str()); while ((encodeRetID = th_encode_flushheader(theoraState, &theoraComment, &packet)) > 0) { // ost::slog(ost::Slog::levelDebug) << "TheoraEncoder:: inserting header/n"; #ifdef DEBUG std::cerr << "Theora Packet Number: "<< packet.packetno<<std::endl; #endif packet.streamType = ogg_theora; packet.streamNo = streamNo; packet.streamHeader = true; headerList.push_back(OggPacket(packet.clone())); } th_comment_clear(&theoraComment); if (encodeRetID == TH_EFAULT) throw "TheoraEncoder::operator <<: encoder or packet are NULL"; } void TheoraEncoder::configureEncoder(TheoraStreamParameter& config, StreamConfig& streamConf, std::vector<OggComment>& oggComments) { if (isConfigured()) throw "TheoraEncoder::setConfig: can't configure encoder twice\n"; // Theora has a divisible-by-sixteen restriction for the encoded video size // scale the frame size up to the nearest /16 and calculate offsets config.frameX = (config.pictureX+15)&~0xF; config.frameY = (config.pictureY+15)&~0xF; // We force the offset to be even. // This ensures that the chroma samples align properly with the luma // samples. config.frameXOffset = ((config.frameX - config.pictureX)/2)&~1; config.frameYOffset = ((config.frameY - config.pictureY)/2)&~1; /* std::cerr << "Picture ("<< config.pictureX<<":"<< config.pictureY <<") Frame ("<< config.frameX << ":"<< config.frameY <<") offset ("<< config.frameXOffset <<":"<<config.frameYOffset<<")\n"; */ // let's initialize the theora encoder th_info_init(&theoraInfo); theoraInfo.pic_width = config.pictureX; theoraInfo.pic_height = config.pictureY; theoraInfo.frame_width = config.frameX; theoraInfo.frame_height = config.frameY; theoraInfo.pic_x = config.frameXOffset; theoraInfo.pic_y = config.frameYOffset; theoraInfo.fps_numerator = config.framerateNum; theoraInfo.fps_denominator = config.framerateDenom; theoraInfo.aspect_numerator = config.aspectRatioNum; theoraInfo.aspect_denominator = config.aspectRatioDenom; theoraInfo.colorspace = TH_CS_UNSPECIFIED; theoraInfo.pixel_fmt = TH_PF_420; //TH_PF_420; //YUV-4:2:0 theoraInfo.target_bitrate = config.videoBitrate; theoraInfo.quality = config.videoQuality; theoraInfo.keyframe_granule_shift = config.keyframeShift; // 6 bit to distinguish interframes /* create a new theora encoder handle */ theoraState = th_encode_alloc(&theoraInfo); if (theoraState) setConfigured(); else throw "TheoraEncoder::setConfig: Parameters invalid"; /* create our own config */ streamConf.parameter = new TheoraStreamParameter(config); streamConf.type = ogg_theora; streamConf.numOfHeaderPackets = streamConf.headerList.size(); streamConf.streamNo = streamNo; streamConf.serialNo = rand(); createHeader(streamConf.headerList, oggComments); } MediaInputEncoder& TheoraEncoder::operator >>(OggPacket& packet) { if (packetList.empty()) throw "TheoraEncoder::operator >>: No PacketAvailable"; packet = packetList.front(); packetList.pop_front(); if (packetList.empty()) setEmpty(); return(*this); } MediaInputEncoder& TheoraEncoder::operator <<(th_ycbcr_buffer buffer) { if (!isConfigured()) throw "TheoraEncoder::operator <<: codec not configured\n"; int32 errID; if ((errID = th_encode_ycbcr_in(theoraState, buffer)) != 0) { if (errID == TH_EFAULT) throw "TheoraEncoder::operator <<: encoder or video buffer is NULL\n"; if (errID == TH_EINVAL) { std::cerr << "Size of picture "<<buffer[0].width << " x " << buffer[0].height<< " encoder wants " << std::endl; throw "TheoraEncoder::operator <<: buffer size does not match the frame size the encoder was initialized with, or encoding has already completed\n"; } } int32 encodeRetID; while ((encodeRetID = th_encode_packetout(theoraState, 0, &packet)) > 0) { #ifdef DEBUG std::cerr << "Theora Packet Number: "<< packet.packetno<<std::endl; #endif packet.streamType = ogg_theora; packet.streamNo = streamNo; packet.streamHeader = false; packetList.push_back(OggPacket(packet.clone())); // This is not really nice, but there is no function available to tell us // if any data is waiting in advance, so we have to allocate memory, even, // if there might not be any data any more //packet = new OggPacketInternal; } // anyway the last packet request will always fail, so forget the last packet //delete packet; if (encodeRetID == TH_EFAULT) throw "TheoraEncoder::operator <<: encoder or packet are NULL"; setAvailable(); return(*this); } #endif