int FLVEncoder::EncodeVideo() { timeval prev; //Start Log(">FLVEncoder encode video\n"); //Allocate media frame RTMPVideoFrame frame(0,262143); //Check codec switch(videoCodec) { case VideoCodec::SORENSON: //Ser Video codec frame.SetVideoCodec(RTMPVideoFrame::FLV1); break; case VideoCodec::H264: //Ser Video codec frame.SetVideoCodec(RTMPVideoFrame::AVC); //Set NAL type frame.SetAVCType(RTMPVideoFrame::AVCNALU); //No delay frame.SetAVCTS(0); break; default: return Error("-Wrong codec type %d\n",videoCodec); } //Create the encoder VideoEncoder *encoder = VideoCodecFactory::CreateEncoder(videoCodec,videoProperties); ///Set frame rate encoder->SetFrameRate(fps,bitrate,intra); //Set dimensions encoder->SetSize(width,height); //Start capturing videoInput->StartVideoCapture(width,height,fps); //The time of the first one gettimeofday(&prev,NULL); //No wait for first DWORD frameTime = 0; Log(">FLVEncoder encode vide\n"); //Mientras tengamos que capturar while(encodingVideo) { //Nos quedamos con el puntero antes de que lo cambien BYTE* pic=videoInput->GrabFrame(frameTime); //Ensure we are still encoding if (!encodingVideo) break; //Check pic if (!pic) continue; //Check if we need to send intra if (sendFPU) { //Set it encoder->FastPictureUpdate(); //Do not send anymore sendFPU = false; } //Encode next frame VideoFrame *encoded = encoder->EncodeFrame(pic,videoInput->GetBufferSize()); //Check if (!encoded) break; //Check size if (frame.GetMaxMediaSize()<encoded->GetLength()) { //Not enougth space Error("Not enought space to copy FLV encodec frame [frame:%d,encoded:%d",frame.GetMaxMediaSize(),encoded->GetLength()); //NExt continue; } //Check if (frameTime) { timespec ts; //Lock pthread_mutex_lock(&mutex); //Calculate timeout calcAbsTimeout(&ts,&prev,frameTime); //Wait next or stopped int canceled = !pthread_cond_timedwait(&cond,&mutex,&ts); //Unlock pthread_mutex_unlock(&mutex); //Check if we have been canceled if (canceled) //Exit break; } //Set sending time of previous frame getUpdDifTime(&prev); //Set timestamp encoded->SetTimestamp(getDifTime(&first)/1000); //Set next one frameTime = 1000/fps; //Set duration encoded->SetDuration(frameTime); //Get full frame frame.SetVideoFrame(encoded->GetData(),encoded->GetLength()); //Set buffer size frame.SetMediaSize(encoded->GetLength()); //Check type if (encoded->IsIntra()) //Set type frame.SetFrameType(RTMPVideoFrame::INTRA); else //Set type frame.SetFrameType(RTMPVideoFrame::INTER); //If we need desc but yet not have it if (!frameDesc && encoded->IsIntra() && videoCodec==VideoCodec::H264) { //Create new description AVCDescriptor desc; //Set values desc.SetConfigurationVersion(1); desc.SetAVCProfileIndication(0x42); desc.SetProfileCompatibility(0x80); desc.SetAVCLevelIndication(0x0C); desc.SetNALUnitLength(3); //Get encoded data BYTE *data = encoded->GetData(); //Get size DWORD size = encoded->GetLength(); //get from frame desc.AddParametersFromFrame(data,size); //Crete desc frame frameDesc = new RTMPVideoFrame(getDifTime(&first)/1000,desc); //Lock pthread_mutex_lock(&mutex); //Send it SendMediaFrame(frameDesc); //unlock pthread_mutex_unlock(&mutex); } //Lock pthread_mutex_lock(&mutex); //Set timestamp frame.SetTimestamp(encoded->GetTimeStamp()); //Publish it SendMediaFrame(&frame); //For each listener for(MediaFrameListeners::iterator it = mediaListeners.begin(); it!=mediaListeners.end(); ++it) //Send it (*it)->onMediaFrame(RTMPMediaStream::id,*encoded); //unlock pthread_mutex_unlock(&mutex); } Log("-FLVEncoder encode video end of loop\n"); //Stop the capture videoInput->StopVideoCapture(); //Check if (encoder) //Exit delete(encoder); Log("<FLVEncoder encode vide\n"); //Exit return 1; }
/**************************************** * RecVideo * Obtiene los packetes y los muestra *****************************************/ int MediaBridgeSession::RecVideo() { //Coders VideoDecoder* decoder = NULL; VideoEncoder* encoder = VideoCodecFactory::CreateEncoder(VideoCodec::SORENSON); //Create new video frame RTMPVideoFrame frame(0,262143); //Set codec frame.SetVideoCodec(RTMPVideoFrame::FLV1); int width=0; int height=0; DWORD numpixels=0; Log(">RecVideo\n"); //Mientras tengamos que capturar while(receivingVideo) { ///Obtenemos el paquete RTPPacket* packet = rtpVideo.GetPacket(); //Check if (!packet) //Next continue; //Get type VideoCodec::Type type = (VideoCodec::Type)packet->GetCodec(); if ((decoder==NULL) || (type!=decoder->type)) { //Si habia uno nos lo cargamos if (decoder!=NULL) delete decoder; //Creamos uno dependiendo del tipo decoder = VideoCodecFactory::CreateDecoder(type); //Check if (!decoder) { delete(packet); continue; } } //Lo decodificamos if(!decoder->DecodePacket(packet->GetMediaData(),packet->GetMediaLength(),0,packet->GetMark())) { delete(packet); continue; } //Get mark bool mark = packet->GetMark(); //Delete packet delete(packet); //Check if it is last one if(!mark) continue; //Check size if (decoder->GetWidth()!=width || decoder->GetHeight()!=height) { //Get dimension width = decoder->GetWidth(); height = decoder->GetHeight(); //Set size numpixels = width*height*3/2; //Set also frame rate and bps encoder->SetFrameRate(25,300,500); //Set them in the encoder encoder->SetSize(width,height); } //Encode next frame VideoFrame *encoded = encoder->EncodeFrame(decoder->GetFrame(),numpixels); //Check if (!encoded) break; //Check size if (frame.GetMaxMediaSize()<encoded->GetLength()) //Not enougth space return Error("Not enought space to copy FLV encodec frame [frame:%d,encoded:%d",frame.GetMaxMediaSize(),encoded->GetLength()); //Get full frame frame.SetVideoFrame(encoded->GetData(),encoded->GetLength()); //Set buffer size frame.SetMediaSize(encoded->GetLength()); //Check type if (encoded->IsIntra()) //Set type frame.SetFrameType(RTMPVideoFrame::INTRA); else //Set type frame.SetFrameType(RTMPVideoFrame::INTER); //Let the connection set the timestamp frame.SetTimestamp(getDifTime(&first)/1000); //Send it SendMediaFrame(&frame); } //Check if (decoder) //Delete delete(decoder); //Check if (encoder) //Delete delete(encoder); Log("<RecVideo\n"); }
/******************************************* * SendVideo * Capturamos el video y lo mandamos *******************************************/ int VideoStream::SendVideo() { timeval prev; timeval lastFPU; DWORD num = 0; QWORD overslept = 0; Acumulator bitrateAcu(1000); Acumulator fpsAcu(1000); Log(">SendVideo [width:%d,size:%d,bitrate:%d,fps:%d,intra:%d]\n",videoGrabWidth,videoGrabHeight,videoBitrate,videoFPS,videoIntraPeriod); //Creamos el encoder VideoEncoder* videoEncoder = VideoCodecFactory::CreateEncoder(videoCodec,videoProperties); //Comprobamos que se haya creado correctamente if (videoEncoder == NULL) //error return Error("Can't create video encoder\n"); //Comrpobamos que tengamos video de entrada if (videoInput == NULL) return Error("No video input"); //Iniciamos el tama�o del video if (!videoInput->StartVideoCapture(videoGrabWidth,videoGrabHeight,videoFPS)) return Error("Couldn't set video capture\n"); //Start at 80% int current = videoBitrate*0.8; //Send at higher bitrate first frame, but skip frames after that so sending bitrate is kept videoEncoder->SetFrameRate(videoFPS,current*5,videoIntraPeriod); //No wait for first QWORD frameTime = 0; //Iniciamos el tamama�o del encoder videoEncoder->SetSize(videoGrabWidth,videoGrabHeight); //The time of the previos one gettimeofday(&prev,NULL); //Fist FPU gettimeofday(&lastFPU,NULL); //Started Log("-Sending video\n"); //Mientras tengamos que capturar while(sendingVideo) { //Nos quedamos con el puntero antes de que lo cambien BYTE *pic = videoInput->GrabFrame(frameTime/1000); //Check picture if (!pic) //Exit continue; //Check if we need to send intra if (sendFPU) { //Do not send anymore sendFPU = false; //Do not send if we just send one (100ms) if (getDifTime(&lastFPU)/1000>minFPUPeriod) { //Send at higher bitrate first frame, but skip frames after that so sending bitrate is kept videoEncoder->SetFrameRate(videoFPS,current*5,videoIntraPeriod); //Reste frametime so it is calcualted afterwards frameTime = 0; //Set it videoEncoder->FastPictureUpdate(); //Update last FPU getUpdDifTime(&lastFPU); } } //Calculate target bitrate int target = current; //Check temporal limits for estimations if (bitrateAcu.IsInWindow()) { //Get real sent bitrate during last second and convert to kbits DWORD instant = bitrateAcu.GetInstantAvg()/1000; //If we are in quarentine if (videoBitrateLimitCount) //Limit sending bitrate target = videoBitrateLimit; //Check if sending below limits else if (instant<videoBitrate) //Increase a 8% each second or fps kbps target += (DWORD)(target*0.08/videoFPS)+1; } //Check target bitrate agains max conf bitrate if (target>videoBitrate*1.2) //Set limit to max bitrate allowing a 20% overflow so instant bitrate can get closer to target target = videoBitrate*1.2; //Check limits counter if (videoBitrateLimitCount>0) //One frame less of limit videoBitrateLimitCount--; //Check if we have a new bitrate if (target && target!=current) { //Reset bitrate videoEncoder->SetFrameRate(videoFPS,target,videoIntraPeriod); //Upate current current = target; } //Procesamos el frame VideoFrame *videoFrame = videoEncoder->EncodeFrame(pic,videoInput->GetBufferSize()); //If was failed if (!videoFrame) //Next continue; //Increase frame counter fpsAcu.Update(getTime()/1000,1); //Check if (frameTime) { timespec ts; //Lock pthread_mutex_lock(&mutex); //Calculate slept time QWORD sleep = frameTime; //Remove extra sleep from prev if (overslept<sleep) //Remove it sleep -= overslept; else //Do not overflow sleep = 1; //Calculate timeout calcAbsTimeoutNS(&ts,&prev,sleep); //Wait next or stopped int canceled = !pthread_cond_timedwait(&cond,&mutex,&ts); //Unlock pthread_mutex_unlock(&mutex); //Check if we have been canceled if (canceled) //Exit break; //Get differencence QWORD diff = getDifTime(&prev); //If it is biffer if (diff>frameTime) //Get what we have slept more overslept = diff-frameTime; else //No oversletp (shoulddn't be possible) overslept = 0; } //Increase frame counter fpsAcu.Update(getTime()/1000,1); //If first if (!frameTime) { //Set frame time, slower frameTime = 5*1000000/videoFPS; //Restore bitrate videoEncoder->SetFrameRate(videoFPS,current,videoIntraPeriod); } else { //Set frame time frameTime = 1000000/videoFPS; } //Add frame size in bits to bitrate calculator bitrateAcu.Update(getDifTime(&ini)/1000,videoFrame->GetLength()*8); //Set frame timestamp videoFrame->SetTimestamp(getDifTime(&ini)/1000); //Check if we have mediaListener if (mediaListener) //Call it mediaListener->onMediaFrame(*videoFrame); //Set sending time of previous frame getUpdDifTime(&prev); //Calculate sending times based on bitrate DWORD sendingTime = videoFrame->GetLength()*8/current; //Adjust to maximum time if (sendingTime>frameTime/1000) //Cap it sendingTime = frameTime/1000; //If it was a I frame if (videoFrame->IsIntra()) //Clean rtp rtx buffer rtp.FlushRTXPackets(); //Send it smoothly smoother.SendFrame(videoFrame,sendingTime); //Dump statistics if (num && ((num%videoFPS*10)==0)) { Debug("-Send bitrate target=%d current=%d avg=%llf rate=[%llf,%llf] fps=[%llf,%llf] limit=%d\n",target,current,bitrateAcu.GetInstantAvg()/1000,bitrateAcu.GetMinAvg()/1000,bitrateAcu.GetMaxAvg()/1000,fpsAcu.GetMinAvg(),fpsAcu.GetMaxAvg(),videoBitrateLimit); bitrateAcu.ResetMinMax(); fpsAcu.ResetMinMax(); } num++; } Log("-SendVideo out of loop\n"); //Terminamos de capturar videoInput->StopVideoCapture(); //Check if (videoEncoder) //Borramos el encoder delete videoEncoder; //Salimos Log("<SendVideo [%d]\n",sendingVideo); return 0; }