EVOLUTION-MANAGER
Edit File: oggResize.cpp
/* * oggResize creates a resized video * * Copyright (C) 2008-2009 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. * */ #ifdef __WIN32 #define __GNU_LIBRARY__ #include "../win32/getopt_win.h" #endif #include <iostream> #include <map> #include <vector> #include <string> #include <sstream> #include <cstring> #include <cstdlib> #include <cmath> #include <ctime> #include <unistd.h> #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "definition.h" #include "th_helper.h" #include "rgbPlane.h" #include "pictureBlend.h" #include "pictureResize.h" #include "pictureLoader.h" #include "audioConverter.h" #include "oggComment.h" #include "theoraEncoder.h" #include "theoraDecoder.h" #include "theoraStreamParameter.h" #include "vorbisEncoder.h" #include "vorbisDecoder.h" #include "vorbisStreamParameter.h" #include "fileRepository.h" #include "streamSerializer.h" #include "streamMux.h" #include "blendElement.h" #include "cmdlineextractor.h" void printHelpScreen ( const std::string& progname ) { std::cerr << "usage: "<<progname << " -- package and version \"" << PACKAGE_STRING << "\"\n\n" << " [Options] originalFile.ogv newFile.ogv\n" << " Option: \n" << " -h this helpscreen\n" << " -s <width x height> new frame size\n" << " -f video framerate in frames per second\n" << " -F audio samplerate in Hz\n" << " -d video datarate in Bit/s\n" << " -D audio datarate in Bit/s\n" << " -q resize quality (1=best/slow; 6=worst/fast)\n" << " -N audio channels\n" << " -a add png with alpha channel on top of a frame\n" << " before the resize process\n" << " -A add png with alpha channel on top of a frame\n" << " after the resize process\n" << " -p only use every <x>th packet to create the new video\n" << " -c comments for the video stream\n" << " -C comments for the audio stream\n" << " -t stretch picture to new size\n\n"; } /* you can create a alpha blend object with the following option * -a picturex.png,1.23,2.34;picturey.png,5.12,7,s */ void alphaBlend ( double time, RGBPlane& inPlane, std::vector<BlendElement>& blendList, float intensityStair ) { for ( uint32 i ( 0 ); i<blendList.size(); ++i ) { switch ( blendList[i].state ) { case BlendElement::blend_off: { if ( time >= blendList[i].startTime ) { if ( blendList[i].smooth == true ) { blendList[i].state = BlendElement::blend_slideIn; } else { blendList[i].intensity = 1.0; blendList[i].state = BlendElement::blend_on; } } } break; case BlendElement::blend_slideIn: { blendList[i].intensity += intensityStair; if ( blendList[i].intensity >= 1.0 ) { blendList[i].state = BlendElement::blend_on; blendList[i].intensity = 1.0; } } break; case BlendElement::blend_on: { if ( ( blendList[i].endTime > 0.0 ) && ( time >= blendList[i].endTime ) ) { if ( blendList[i].smooth == true ) { blendList[i].state = BlendElement::blend_slideOut; } else { blendList[i].intensity = 0.0; blendList[i].state = BlendElement::blend_end; } } } break; case BlendElement::blend_slideOut: { blendList[i].intensity -= intensityStair; if ( blendList[i].intensity <= 0.0 ) { blendList[i].state = BlendElement::blend_end; blendList[i].intensity = 0.0; } } break; case BlendElement::blend_end: { /* do nothing */ } break; } if ( ( blendList[i].state != BlendElement::blend_end ) && ( blendList[i].state != BlendElement::blend_off ) ) inPlane = PictureBlend::alphaBlend ( inPlane, blendList[i].picture, blendList[i].intensity ); } } int main ( int argc, char* argv[] ) { bool copyTheora ( true ); bool copyVorbis ( true ); uint32 width ( 480 ); uint32 height ( 320 ); uint32 offsetX; uint32 offsetY; bool changeSize ( false ); uint32 framerateNum ( 25 ); uint32 framerateDenom ( 1 ); bool changeFramerate ( false ); uint32 videoDatarate ( 256000 ); bool changeVideoDatarate ( false ); uint32 audioDatarate ( 64000 ); bool changeAudioDatarate ( false ); uint32 audioSamplerate ( 44100 ); bool changeAudioSamplerate ( false ); uint32 audioChannels ( 2 ); bool changeAudioChannels ( false ); bool strech ( false ); bool withBlend ( false ); bool ignoreVorbis (false ); uint32 preview(1); uint32 pictureCounter(0); uint8 quality(2); std::vector<OggComment> videoComments; bool withVideoComments ( false ); std::vector<OggComment> audioComments; bool withAudioComments ( false ); std::vector<BlendElement> blendListBefore; std::vector<BlendElement> blendListAfter; TheoraStreamParameter theoraConfigOutput; VorbisStreamParameter vorbisConfigOutput; TheoraStreamParameter theoraConfigInput; VorbisStreamParameter vorbisConfigInput; std::string programName ( argv[0] ); srand ( time ( 0 ) ); int opt; while ( ( opt = getopt ( argc, argv, "hs:f:d:tD:c:C:N:F:a:A:q:p:" ) ) != EOF ) switch ( opt ) { case 'h': case '?': printHelpScreen ( programName ); exit ( -1 ); case 'a': { CmdlineExtractor::extractBlend ( blendListBefore, optarg, ':', ',' ); copyTheora = false; } break; case 'A': { CmdlineExtractor::extractBlend ( blendListAfter, optarg, ':', ',' ); copyTheora = false; } break; case 'q': { uint8 _quality = atoi(optarg); if (_quality < 1) _quality = 1; if (_quality > 5) _quality = 5; // non linear switch (_quality) { case 1: quality = 2; break; case 2: quality = 3; break; case 3: quality = 4; break; case 4: quality = 6; break; case 5: quality = 10; break; } break; } case 's': { std::deque<uint32> framesize; CmdlineExtractor::extractUint32 ( framesize, optarg, 'x' ); if ( framesize.size() != 2 ) { std::cerr << "please specify the size in the following way: -s320x480\n"; exit ( -1 ); } changeSize = true; width = framesize[0]; height = framesize[1]; } break; case 'f': { std::deque<uint32> framerate; CmdlineExtractor::extractUint32 ( framerate, optarg, ':' ); if ( framerate.size() == 1 ) { changeFramerate = true; framerateNum = framerate[0]; framerateDenom = 1; break; } if ( framerate.size() == 2 ) { changeFramerate = true; framerateNum = framerate[0]; framerateDenom = ( framerate[1] == 0 ) ? 1 : framerate[1]; break; } std::cerr << "please specify the framerate in the following way -s25:2 or -s24\n"; exit ( -1 ); } break; case 'd': changeVideoDatarate = true; videoDatarate = atoi ( optarg ); // yes, I know the atoi bug break; case 'D': changeAudioDatarate = true; audioDatarate = atoi ( optarg ); break; case 'c': withVideoComments = true; CmdlineExtractor::extractCommentPairs ( videoComments, optarg, ';', '=' ); break; case 'C': withAudioComments = true; CmdlineExtractor::extractCommentPairs ( audioComments, optarg, ';', '=' ); break; case 'N': changeAudioChannels = true; audioChannels = atoi ( optarg ); if ( ( audioChannels != 1 ) && ( audioChannels != 2 ) ) changeAudioChannels = false; break; case 'F': changeAudioSamplerate = true; audioSamplerate = atoi ( optarg ); break; case 't': strech = true; break; case 'p': preview = atoi(optarg); copyTheora = false; ignoreVorbis = true; break; } argc -= optind; argv += optind; if ( ( argc < 2 ) ) { printHelpScreen ( programName ); return ( -1 ); } std::string inputFile = std::string ( argv[0] ); std::string outputFile = std::string ( argv[1] ); /* create configuration */ StreamSerializer inStream; if ( !inStream.open ( inputFile ) ) { std::cerr << "can not open file <"<<inputFile<<">\n"; return ( -1 ); } /* get all information from the stream */ std::vector<StreamConfig> inFileConfigList; std::vector<StreamConfig> outFileConfigList; inStream.getStreamConfig ( inFileConfigList ); bool foundTheora ( false ); int8 theoraStreamID ( -1 ); int8 outTheoraStreamID ( -1 ); bool foundVorbis ( false ); int8 vorbisStreamID ( -1 ); int8 outVorbisStreamID ( -1 ); /* Output some stream information */ std::cerr << "Input Streams: \n-----------\n"; for ( uint32 i ( 0 ); i<inFileConfigList.size(); ++i ) { StreamConfig& conf ( inFileConfigList[i] ); std::cerr << "Stream No: "<< ( int ) conf.streamNo<<" serial ID (0x" <<std::hex << conf.serialNo<<std::dec<<")\n"; if ( conf.parameter ) std::cerr << conf.parameter->toString(); else std::cerr << "unknown -> stream will be ignored\n"; if ( ( conf.type == ogg_theora ) && ( foundTheora == false ) ) { foundTheora = true; theoraStreamID = i; theoraConfigInput = * ( TheoraStreamParameter* ) ( conf.parameter ); } if ( ( conf.type == ogg_vorbis ) && ( foundVorbis == false ) && ( ignoreVorbis == false )) { foundVorbis = true; vorbisStreamID = i; vorbisConfigInput = * ( VorbisStreamParameter* ) ( conf.parameter ); } } /* firstly copy all paramters */ theoraConfigOutput = theoraConfigInput; vorbisConfigOutput = vorbisConfigInput; if ( changeVideoDatarate ) { if ( theoraConfigInput.videoBitrate != videoDatarate ) { theoraConfigOutput.videoBitrate = videoDatarate; theoraConfigOutput.videoQuality = 0; copyTheora = false; } } if ( changeSize ) { if ( ( theoraConfigInput.pictureX != width ) || ( theoraConfigInput.pictureY != height ) || ( theoraConfigInput.aspectRatioNum != 1 ) || ( theoraConfigInput.aspectRatioDenom != 1 ) ) { theoraConfigOutput.pictureX = width; theoraConfigOutput.pictureY = height; /* no reason for using another aspect ratio than 1:1, are there? */ theoraConfigOutput.aspectRatioDenom = 1; theoraConfigOutput.aspectRatioNum = 1; copyTheora = false; } } if ( changeFramerate ) { if ( ( ( theoraConfigOutput.framerateNum != framerateNum ) || ( theoraConfigOutput.framerateDenom != framerateDenom ) ) && ( ( theoraConfigOutput.framerateNum*1.0 ) / ( theoraConfigOutput.framerateDenom*1.0 ) != ( framerateNum*1.0 ) / ( framerateDenom*1.0 ) ) ) { theoraConfigOutput.framerateNum = framerateNum; theoraConfigOutput.framerateDenom = framerateDenom; copyTheora = false; } } if ( changeAudioDatarate ) { if ( vorbisConfigOutput.datarate != audioDatarate ) { vorbisConfigOutput.datarate = audioDatarate; copyVorbis = false; } } if ( changeAudioSamplerate ) { if ( vorbisConfigOutput.samplerate != audioSamplerate ) { vorbisConfigOutput.samplerate = audioSamplerate; copyVorbis = false; } } if ( changeAudioChannels ) { if ( vorbisConfigOutput.channels != audioChannels ) { vorbisConfigOutput.channels = audioChannels; copyVorbis = false; } } /* create Decoder/Encoder pair, if they are needed */ TheoraDecoder* theoraDecoder ( 0 ); VorbisDecoder* vorbisDecoder ( 0 ); TheoraEncoder* theoraEncoder ( 0 ); VorbisEncoder* vorbisEncoder ( 0 ); uint8 outStreamCounter ( 0 ); if ( foundTheora ) { outTheoraStreamID = outStreamCounter; if ( !copyTheora ) { /* create and initialize the theora decoder */ theoraDecoder = new TheoraDecoder(); theoraDecoder->initDecoder ( inFileConfigList[theoraStreamID], videoComments ); /* create and initialize the theora encoder */ theoraEncoder = new TheoraEncoder ( outStreamCounter ); StreamConfig theoraOutStreamConf; try { theoraEncoder->configureEncoder ( theoraConfigOutput, theoraOutStreamConf, videoComments ); } catch ( const char* data ) { std::cerr << data; exit ( -1 ); } outFileConfigList.push_back ( theoraOutStreamConf ); } else { StreamConfig theoraOutStreamConf = inFileConfigList[theoraStreamID]; theoraOutStreamConf.streamNo = outStreamCounter; outFileConfigList.push_back ( theoraOutStreamConf ); } outStreamCounter++; } if ( foundVorbis ) { outVorbisStreamID = outStreamCounter; if ( !copyVorbis ) { /* create and initialize the theora decoder */ vorbisDecoder = new VorbisDecoder ( vorbisStreamID ); vorbisDecoder->initDecoder ( inFileConfigList[vorbisStreamID], audioComments ); /* create stream configuration */ vorbisEncoder = new VorbisEncoder ( outStreamCounter ); /* this configuration is filled by the encoder */ StreamConfig vorbisOutStreamConf; /* configure the theora encoder and get a stream config back * which configures the stream multiplexer */ try { vorbisEncoder->configureEncoder ( vorbisConfigOutput, vorbisOutStreamConf, audioComments ); } catch ( const char* data ) { std::cerr << data; exit ( -1 ); } outFileConfigList.push_back ( vorbisOutStreamConf ); } else { StreamConfig vorbisOutStreamConf = inFileConfigList[vorbisStreamID]; vorbisOutStreamConf.streamNo = outStreamCounter; outFileConfigList.push_back ( vorbisOutStreamConf ); // outFileConfigList.push_back ( inFileConfigList[vorbisStreamID] ); } outStreamCounter++; } /* create a repository, where the data should be placed */ FileRepository* repository = new FileRepository( outputFile, MediaUnit::write ); /* create a stream multiplexer */ StreamMux streamCreate ( repository ); /* Print out the output information */ std::cerr <<"Output Stream:\n-------------\n"; for ( uint32 i ( 0 ); i<outFileConfigList.size(); ++i ) { StreamConfig& conf ( outFileConfigList[i] ); std::cerr << "Stream No: "<< ( int ) conf.streamNo<<" serial ID (0x" <<std::hex << conf.serialNo<<std::dec<<")\n"; if ( conf.parameter ) std::cerr << conf.parameter->toString(); else std::cerr << "unknown \n"; } /* configure the stream multiplexer */ streamCreate.configureStreams ( outFileConfigList ); OggPacket packet; OggPacket newPacket; double time; double distance ( ( 1.0*theoraConfigOutput.framerateDenom ) / ( 1.0 *theoraConfigOutput.framerateNum ) ); double nextTime ( 0 ); th_ycbcr_buffer inycbcr; th_ycbcr_buffer outycbcr; /* cleanup buffers */ th_clean_ycbcr(inycbcr); th_clean_ycbcr(outycbcr); RGBPlane inPlane; RGBPlane outPlane; AudioPacket audioPacket; AudioPacket newAudioPacket; AudioConverter converter; if ( foundVorbis ) { converter.initResample ( vorbisConfigOutput.channels, ( vorbisConfigOutput.samplerate*1.0 ) / ( vorbisConfigInput.samplerate *1.0 ) ); } float intensityStair = ( theoraConfigOutput.framerateDenom*1.0 ) / ( theoraConfigOutput.framerateNum*1.0 ); double aspectCorrection = (theoraDecoder->getInfo().aspect_numerator*1.0)/(theoraDecoder->getInfo().aspect_denominator*1.0); std::cerr << "Aspect Ratio correction: "<<aspectCorrection<<std::endl; while ( inStream.available() ) { time = inStream.getNextPacket ( packet ); std::cerr << " "<<time<<" \r";// << inycbcr[0].width << " "<<inycbcr[0].stride<<" "<<widthIn<<" \r"; if ( packet.getStreamNo() == theoraStreamID ) { // std::cerr << "theora \n"; if ( copyTheora ) { packet.setStreamNo ( outTheoraStreamID ); streamCreate << packet; } else { try { ( *theoraDecoder ) << packet; ( *theoraDecoder ) >> inycbcr; while ( (uint64)(time*1000.0+0.5) >= (uint64)(nextTime*1000.0+0.5) ) { inPlane = PictureLoader::importYCrCb_theora ( inycbcr, theoraDecoder->getWidth(), theoraDecoder->getHeight(), theoraDecoder->getInfo().pic_x, theoraDecoder->getInfo().pic_y ); /* should be an alpha blend applied before resizing */ if ( !blendListBefore.empty() ) { alphaBlend ( time, inPlane, blendListBefore, intensityStair ); } if ( changeSize ) { if (strech) inPlane = PictureResize::resize ( inPlane, width, height, quality ); else inPlane = PictureResize::reframe ( inPlane, width, height, quality, 0, aspectCorrection ); } /* should be an alpha blend applied after resizing? */ if ( !blendListAfter.empty() ) { alphaBlend ( time, inPlane, blendListAfter, intensityStair ); } if ( ( !blendListBefore.empty() ) || ( !blendListAfter.empty() ) || changeSize ) { /* there are changes written to the outycbcr */ PictureLoader::exportYCrCb_theora ( inPlane, outycbcr); if (pictureCounter++%preview == 0) ( *theoraEncoder ) << outycbcr; } else { /* use the original data */ if (pictureCounter++%preview == 0) ( *theoraEncoder ) << inycbcr; } if (theoraEncoder->isAvailable()) { ( *theoraEncoder ) >> packet; // std::cerr << "Theora Encoder granule position "<< packet.granulepos()<<"\n"; streamCreate << packet; } nextTime += distance; } // std::cerr << std::endl; } catch ( const char* error ) { std::cerr << "Exception: " << error; } } } if ( packet.getStreamNo() == vorbisStreamID ) { // std::cerr << "vorbis\n"; if ( copyVorbis ) { packet.setStreamNo ( outVorbisStreamID ); streamCreate << packet; } else { // relevant packet try { ( *vorbisDecoder ) << packet; while ( vorbisDecoder->isAvailable() ) { ( *vorbisDecoder ) >> audioPacket; if ( changeAudioSamplerate ) { AudioPacket tmp; static uint64 sampleCount ( 0 ); static uint64 sampleCount2 ( 0 ); sampleCount += ( *audioPacket )->getLength(); if ( converter.resample ( audioPacket,tmp ) ) { sampleCount2 += ( *tmp )->getLength(); ( *vorbisEncoder ) << tmp; } } else { ( *vorbisEncoder ) << audioPacket; } while ( vorbisEncoder->isAvailable() ) { OggPacket pckt; ( *vorbisEncoder ) >> pckt; streamCreate << pckt; } } } catch ( char* error ) { std::cerr << "Exception: " << error; } } } } converter.resampleflush(audioPacket); streamCreate.setEndOfStream(); th_free_ycbcr(outycbcr); /* do cleanup */ delete theoraDecoder; delete vorbisDecoder; delete theoraEncoder; delete vorbisEncoder; /* close the sample converter */ converter.closeResample(); streamCreate.close(); inStream.close(); std::cerr << std::endl; }