Mixer.cpp 14.3 KB
/* 
 * File:   Mixer.cpp
 * Author: eduardo
 * 
 * Created on 17 de Janeiro de 2012, 15:28
 */
#include "Mixer.h"

/* Construtores e destrutores...*/
Mixer::Mixer() {
    this->setNumThreads("1");
    PRINTL(util::_DEBUG, "Mixer Done!\n");     
}
Mixer::Mixer(string mainVideo, string secondaryVideo) {
    this->setMainVideo(mainVideo);
    this->setSecondaryVideo(secondaryVideo);
    this->setNumThreads("1");
    PRINTL(util::_DEBUG, "Done!\n");             
}
Mixer::~Mixer() {
    PRINTL(util::_DEBUG, "Mixer finalized!\n");   
}
/* FIM de Construtores e destrutores...*/

/*Faz a chamada ffmpeg no terminal.*/

void Mixer::initialize(string mainVideo, string slVideo, int positionSecondaryVideo, int sizeSecondaryVideo,
    int transparency, char* _id, char* path_uploads, char* path_contents){
    
    PRINTL(util::_INFO, "Mixando...\n");
    uploads = path_uploads;
    contents = path_contents;
    stringstream ss;
	ss << _id;
	ss >> user_id;

    this->setMainVideo(mainVideo);
    this->setSecondaryVideo(slVideo);
    this->setNumThreads("8");	//tem que aparecer antes do metodo adjustVideosFps()    
    //this->adjustVideosFps();
    
    this->setSize(sizeSecondaryVideo);
    this->setPositionSecondaryVideo(positionSecondaryVideo);
    this->setTransparency(transparency);
    this->setPathFinal();
    this->mixVideos();
}

void Mixer::mixVideos () {    
    //convertendo os numeros em string para concatenar à frase
    std::stringstream num1;
    num1 << this->widthSecondaryVideo;
    string num1String;
    num1 >> num1String;

    std::stringstream num2;
    num2 << this->heightSecondaryVideo;
    string num2String;
    num2 >> num2String;

    /*std::stringstream num2;
    num2 << this->heightSecondaryVideo;
    string num2String, aux;
    num2 >> aux;
    num2String = "in_h*"+aux;*/
    
    string strPosition;
    if(this->getPositionSecondaryVideo() == TOP_LEFT)
	strPosition = "10:10"; 
    else if((this->getPositionSecondaryVideo() == TOP_RIGHT))
	strPosition = "main_w-overlay_w-10:10"; 
    else if((this->getPositionSecondaryVideo() == BOTTOM_RIGHT))
	strPosition = "main_w-overlay_w-10:main_h-overlay_h-10";
    else if((this->getPositionSecondaryVideo() == BOTTOM_LEFT))
	strPosition = "10:main_h-overlay_h-10";
    else{	//se não escolheu nenhum inteiro válido, consideramos BOTTOM_RIGHT o local padrão, mostra msg de erro
	strPosition = "main_w-overlay_w-10:main_h-overlay_h-10";
	cout << "####################################################################\n";
	cout << "# Posicao do vídeo de libras é inválido!                           #\n";
	cout << "# Assumiremos a posicao padrao (Bottom_Right)!                     #\n";
	cout << "# OPCOES: 1-Top_Left; 2-Top_Right; 3-Bottom_Right; 4-Bottom_Left;  #\n";
	cout << "####################################################################";
    }

    //retira a string .ts da primeira string
    //string nameOfMainVideo = mainVideo.substr(0,mainVideo.length()-3);
    int dotPosition = 0;	
    for(int k = mainVideo.length(); k >= 0; k--) {
	if (mainVideo[k] == '.') {
		dotPosition = k;
		break;
	}	
    }
    string nameOfMainVideo = mainVideo.substr(0, dotPosition);

    string transparency;
    if(this->getTransparency()==0){
	transparency = "";		
    }
    else
	transparency = "transp";	

    /*string ffmpegSentence = "ffmpeg -i "+this->mainVideo+" -y -vf \"movie="+this->secondaryVideo+", "+
            "scale="+ num1String +":"+num2String+", setpts=PTS-STARTPTS [movie]; "+
            "[in] setpts=PTS-STARTPTS, [movie] overlay"+transparency+"="+strPosition+
            " [out]\" -sameq -threads "+this->numThreads+" "+nameOfMainVideo+"_Libras.ts";*/

    /*string ffmpegSentence = "ffmpeg -i "+this->mainVideo+" -y -vf \"movie="+this->secondaryVideo+", "+
            "scale="+ num1String +":"+num2String+", setpts=PTS-STARTPTS [movie]; "+
            "[in] setpts=PTS-STARTPTS, [movie] overlay"+transparency+"="+strPosition+
            " [out]\" -sameq -ar 22050 -ab 32 -f flv -acodec pcm_s16le -vcodec flv -threads "+this->numThreads+" "+nameOfMainVideo+"_Libras.flv";*/

    //LEONARDO
    /*string ffmpegSentence = "ffmpeg -i "+this->mainVideo+" -y -vf \"movie="+this->secondaryVideo+", "+
            "scale="+ num1String +":"+num2String+", [movie] overlay"+transparency+"="+strPosition+
            " [out]\" -sameq -ar 22050 -ab 32 -f flv -acodec pcm_s16le -vcodec flv -threads "+this->numThreads+" "+nameOfMainVideo+"_Libras.flv";*/

    //TRANSCODIFICAR PARA FLV
    /*
    string ffmpegSentence = "ffmpeg -i "+this->mainVideo+" -y -vf \"movie="+this->secondaryVideo+", "+
            "scale="+ num1String +":"+num2String+", setpts=PTS-STARTPTS, [movie] overlay"+transparency+"="+strPosition+
            " [out]\" -sameq -strict experimental -vcodec mpeg2video -r 30 -threads "+this->numThreads+" "+pathFinal;
    */

    string ffmpegSentence = "ffmpeg -i "+this->mainVideo+" -v quiet -y -vf \"movie="+this->secondaryVideo+", "+
            "scale="+ num1String +":"+num2String+", setpts=PTS-STARTPTS, [movie] overlay"+transparency+"="+strPosition+
            " [out]\" -qscale 0 -strict experimental -vcodec libx264 -preset fast -r 30 -threads "+this->numThreads+" "+pathFinal;



    /*
    string ffmpegSentence = "ffmpeg -i "+this->mainVideo+" -y -vf \"movie="+this->secondaryVideo+", "+
            "scale="+ num1String +":"+num2String+", setpts=PTS-STARTPTS, [movie] overlay"+transparency+"="+strPosition+
            " [out]\" -sameq -threads "+this->numThreads+" "+pathFinal;
    */

    /*
	convertendo e obtendo ótimos resultados de tamanho do vídeo	
	ffmpeg -i videoDemoLibras.ts -vcodec libx264 -f flv -ar 22050 -ab 32 -sameq -threads 8 -y teste.flv	
    */

     //printf("\n\n%s\n\n", ffmpegSentence.c_str());
     system(ffmpegSentence.c_str());

    //string removeVideoCMD = "rm -rf "+nameOfMainVideo+".ts";    //removo o vídeo exemplo-45fps.ts
    //system(removeVideoCMD.c_str());
}

void Mixer::setPathFinal(){
    stringstream ss;
    ss << contents;
    ss >> pathFinal;    
    pathFinal.append("/").append(user_id).append(".mp4");
}

/*Ajusta o FPS do vídeo principal para 45 se preciso...*/
void Mixer::adjustVideosFps(){
    //primeiro executo a linha de comando que me dá todas as informações do vídeo
    string ffmpegSentence = "ffmpeg -i "+this->mainVideo+"  2> "+temporaryTextFile;
    system(ffmpegSentence.c_str());  					//executo o comando ffmpeg que escreve no arquivo temporário

    fpsAndLine arrayDeFps[10];  					//array onde será guardado os fps encontrados
    int qtdadeDeFPSEncontrados = 0;
    this->readFileFPS(arrayDeFps, &qtdadeDeFPSEncontrados);    		//leio o arquivo procurando os FPS disponíveis

    this->convertMainVideoFPS(arrayDeFps, &qtdadeDeFPSEncontrados);  	//converte o fps do vídeo principal
}

/*Lê do arquivo procurando o fps...*/
void Mixer::readFileFPS(fpsAndLine arrayDeFps [], int * qtdadeDeFPSEncontrados){
    ifstream arq(temporaryTextFile.c_str());//abrindo arquivo com info. dos vídeos
    if (arq.is_open()){
        int indiceDoFPSEncontrados = 0;    //indice a ser utilizado no array
        string line;
        while (!arq.eof()){
            getline(arq,line);              //linha lida no arquivo

            int auxProgram = line.find("Program ");    //procura pela string "fps,"
            if(auxProgram >= 0){
                string id = line.substr(auxProgram+10);
                if(atoi(id.c_str()) != 0){   //diferente de 0, a conversão deu blz...
                    arrayDeFps[indiceDoFPSEncontrados].possibleProgramID = id;
                    printf("\n\n\n\n\nID: %s\n\n\n\n\n",id.c_str());
                }
            }

            int aux = line.find("fps,");    //procura pela string "fps,"
            if(aux >= 0){                
                int i;
                int espacos = 0;            //quantidade de espacos encontrados
                for(i = aux;espacos != 2;i--){
                    if(line.at(i) == ' '){  //se for espaço incremente
                        espacos++;
                    }
                }
                string fps = line.substr(i+espacos,aux-i-espacos-1);//extrai o fps da linha
                double fpsDouble = atof(fps.c_str());               //transformo de string pra double

                arrayDeFps[indiceDoFPSEncontrados].fps = fpsDouble;      //armazeno o fps em um array de struct
                arrayDeFps[indiceDoFPSEncontrados].line = line;     //armazeno a linha em um array de struct

                indiceDoFPSEncontrados++;   //achei um fps, preenchi um struct, vou pro prox.
            }
        }
        arq.close();
        //*qtdadeDeFPSEncontrados = indiceDoFPSEncontrados + 1;
	*qtdadeDeFPSEncontrados = indiceDoFPSEncontrados;
    }

}

void Mixer::convertMainVideoFPS(fpsAndLine arrayDeFps [], int * qtdadeDeFPSEncontrados){
    //retira a string .ts da primeira string
    string nameOfMainVideo = mainVideo.substr(0,mainVideo.length()-3);

    int i;
    if(*qtdadeDeFPSEncontrados > 1){
        //cout << "\n############################\nConverter fps do vídeo principal com vários canais!\n############################\n");
        for(i = 0; i < *qtdadeDeFPSEncontrados;i++){
            //procura pela string "Video: h264 (High), yuv420p, 1920x1080 [PAR 1:1 DAR 16:9],"
            int indexMainVideo1 = arrayDeFps[i].line.find("Video: h264 (High)");
	    int indexMainVideo2 = arrayDeFps[i].line.find("yuv420p");
	    int indexMainVideo3 = arrayDeFps[i].line.find("1920x1080");
	    //int indexMainVideo = arrayDeFps[i].line.find("DAR 16:9");
	    
	    //printf("\n\n\n\n\n\n\nPID do canal: %s \n\n\n\n\n\n\n",arrayDeFps[i].possibleProgramID.c_str());
            if(indexMainVideo1 > 0 && indexMainVideo2 > 0 && indexMainVideo3 > 0){     //eis aqui o vídeo principal
                        //printf("\n\n\n\n\n\n\nPID do canal a ser alterado: %s \n\n\n\n\n\n\n",arrayDeFps[i].possibleProgramID.c_str());
			string ffmpegSentence45 = "ffmpeg -i "+this->mainVideo+" -v quiet -y -r 45 -vcodec libx264 -streamid 0:"+arrayDeFps[i].possibleProgramID+
                                                  "-sameq  -threads "+this->numThreads+" "+nameOfMainVideo+"-45fps.ts";
                        system(ffmpegSentence45.c_str());                        
                        //string removeVideoCMD = "rm -rf "+nameOfMainVideo+".ts";    //removo o vídeo exemplo.ts
                        //system(removeVideoCMD.c_str());
                        this->setMainVideo(nameOfMainVideo+"-45fps.ts");                    
		break;
	    }            
        }
    }
    else{
        //cout << "\n############################\nConverter fps do vídeo principal com apenas um canal!\n############################\n");
        string ffmpegSentence45 = "ffmpeg -i "+this->mainVideo+" -v quiet -y -r 45 -vcodec libx264 "
                                      "-sameq  -threads "+this->numThreads+" "+nameOfMainVideo+"-45fps.ts";
      	system(ffmpegSentence45.c_str());
        //string removeVideoCMD = "rm -rf "+nameOfMainVideo+".ts";    //removo o vídeo exemplo.ts
        //system(removeVideoCMD.c_str());
        this->setMainVideo(nameOfMainVideo+"-45fps.ts");
    }
}




/* O valor da largura e altura está na unidade de Pixels.
 * Se o valor da variável for -1, deverá manter a proporção da mesma em relação
 * à outra.
 * Ex.: 600:-1 ==> largura é 600 e a altura será calculada em relação à mesma para
 * manter proporção.
 * Ex.: -1:600 ==> altura é 600 e a largura será calculada em relação à mesma para
 * manter proporção.
 */
void Mixer::setSize(int size){

    string ffprobeSentence = "ffprobe -show_streams "+this->mainVideo+" 2> /dev/null | grep \"height=\" | cut -d'=' -f2 > "+temporaryTextFile;
    system(ffprobeSentence.c_str());

	//printf("\n\n%s\n\n",ffprobeSentence.c_str());


    string heightStr = "324";    			//se não conseguir ler do arquivo a altura será essa.. :(

    ifstream arq(temporaryTextFile.c_str());//abrindo arquivo com info. dos vídeos
    if (arq.is_open()){        
        getline(arq,heightStr);              //lendo o tamanho(altura) do vídeo
        arq.close();
    }

    string removeFileSentence = "rm -rf "+temporaryTextFile;
    //system(removeFileSentence.c_str());

    int height = atoi(heightStr.c_str());


    if(size == SMALL){    
	this->widthSecondaryVideo = -1;
	this->heightSecondaryVideo = (0.3*height);	
    }
    else if(size == MEDIUM){    
	this->widthSecondaryVideo = -1;
	this->heightSecondaryVideo = (0.4*height);
    }
    else if(size == LARGE){    
	this->widthSecondaryVideo = -1;
	this->heightSecondaryVideo = (0.5*height);
    }
    else{	//se não escolheu nenhum inteiro válido, consideramos MEDIUM_WIDTH a largura padrão, mostra msg de erro
	this->widthSecondaryVideo = -1;
	this->heightSecondaryVideo = (0.4*height);
	cout << "################################################\n";
	cout << "# Tamanho do vídeo de libras é inválido!       #\n";
	cout << "# Assumiremos a largura padrao (Medium_Width)! #\n";
	cout << "# OPCOES: 1-Small; 2-Medium; 3-Large;          #\n";
	cout << "################################################";
    }
}

/*Setters e getters...*/
void Mixer::setMainVideo(string mainVideo){
    this->mainVideo = mainVideo;
    //retira a string .ts da primeira string, adiciona o "temp" antes e o ".txt" depois
    //string nameOfMainVideo = mainVideo.substr(0,mainVideo.length()-3);
    int dotPosition = 0;	
    for(int k = mainVideo.length(); k >= 0; k--) {
	if (mainVideo[k] == '.') {
		dotPosition = k;
		break;
	}	
    }
//ajeitar isso depois
    nameOfMainVideo = mainVideo.substr(0, dotPosition);
    
    stringstream ss;
    ss << uploads;
    ss >> temporaryTextFile;
    temporaryTextFile.append("/").append(this->user_id).append("/tamanho.txt");
    //printf("##########temporaryTextFile: %s\n", temporaryTextFile.c_str());
}
string Mixer::getMainVideo(){
    return this->mainVideo;
}
void Mixer::setSecondaryVideo(string secondaryVideo){
    this->secondaryVideo = secondaryVideo;
}
string Mixer::getSecondaryVideo(){
    return this->secondaryVideo;
}
void Mixer::setPositionSecondaryVideo(int positionSecondaryVideo){
    this->positionSecondaryVideo = positionSecondaryVideo;
}
int Mixer::getPositionSecondaryVideo(){
    return this->positionSecondaryVideo;
}
void Mixer::setTransparency(int transparency){
    this->transparency = transparency;
}
int Mixer::getTransparency(){
    return this->transparency;
}
void Mixer::setNumThreads(string numThreads){
    this->numThreads = numThreads;
}
string Mixer::getNumThreads(){
    return this->numThreads;
}