//---------------------------------------------------------------------------// // File: FFMpegVideo.cpp // //---------------------------------------------------------------------------// #include "Base.h" #include "GEEngineGrafico.h" #include "FFMpegVideo.h" #ifdef USE_MPG_PLAYER #define MPG_FORMAT PIX_FMT_RGB565 #define MPG_FORMAT_SIZE 2 //---------------------------------------------------------------------------// // InitSubsytem // //---------------------------------------------------------------------------// void CFFMpegVideo::InitSubsystem() { av_register_all(); } //---------------------------------------------------------------------------// // EndSubsystem // //---------------------------------------------------------------------------// void CFFMpegVideo::EndSubsystem() { } //---------------------------------------------------------------------------// // Init // //---------------------------------------------------------------------------// bool CFFMpegVideo::Init(const char *pszFile) { // Open video file if (av_open_input_file(&m_pFormatCtx, pszFile, NULL, 0, NULL) != 0) { GLOG(("ERR: FFMpeg can't open stream file %s\n", pszFile)); return false; } // Retrieve stream information if (av_find_stream_info(m_pFormatCtx) < 0) { GLOG(("ERR: FFMpeg can't retrieve stream information for file %s\n", pszFile)); return false; } // Output debug information (solo en debug) #ifdef _DEBUG dump_format(m_pFormatCtx, 0, pszFile, false); #endif // Find the first video stream m_VideoStream = -1; for (int i=0; i < m_pFormatCtx->nb_streams; i++) { if(m_pFormatCtx->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO) { m_VideoStream = i; break; } } if (m_VideoStream == -1) { GLOG(("ERR: FFMpeg can't find video strem on file %s\n", pszFile)); return false; } // Get a pointer to the codec context for the video stream m_pCodecCtx = &m_pFormatCtx->streams[m_VideoStream]->codec; // Find the decoder for the video stream AVCodec *pCodec= avcodec_find_decoder(m_pCodecCtx->codec_id); if (!pCodec) { pCodec = avcodec_find_decoder(CODEC_ID_NONE); if (!pCodec) { GLOG(("ERR: FFMpeg can't find the decoder needed for file %s\n", pszFile)); return false; } } // Inform the codec that we can handle truncated bitstreams -- i.e., // bitstreams where frame boundaries can fall in the middle of packets if (pCodec->capabilities & CODEC_CAP_TRUNCATED) m_pCodecCtx->flags |= CODEC_FLAG_TRUNCATED; // Open codec if(avcodec_open(m_pCodecCtx, pCodec) < 0) { GLOG(("ERR: FFMpeg can't open codec for file %s\n", pszFile)); return false; } // Hack to correct wrong frame rates that seem to be generated by some // codecs if (m_pCodecCtx->frame_rate > 1000 && m_pCodecCtx->frame_rate_base == 1) m_pCodecCtx->frame_rate_base = 1000; // Datos m_iWidth = m_pCodecCtx->width; m_iHeight = m_pCodecCtx->height; m_fLength = (float)m_pFormatCtx->duration / AV_TIME_BASE; m_fStart = (float)m_pFormatCtx->start_time / AV_TIME_BASE; m_fEnd = m_fStart + m_fLength; // Allocate an AVFrame structure m_pFrame = avcodec_alloc_frame(); m_pFrameRGB = avcodec_alloc_frame(); bytesRemaining = 0; packet.data = NULL; m_iCachedFrame = -1; m_iCurrentFrame= -1; m_iFrame = 0; m_bOk = true; return IsOk(); } //---------------------------------------------------------------------------// // End // //---------------------------------------------------------------------------// void CFFMpegVideo::End() { if (IsOk()) { av_free (m_pFrame); av_free (m_pFrameRGB); avcodec_close (m_pCodecCtx); av_close_input_file(m_pFormatCtx); } } //---------------------------------------------------------------------------// // Run // //---------------------------------------------------------------------------// void CFFMpegVideo::Run(float fTime) { m_iFrame = long(fTime * m_pCodecCtx->frame_rate / m_pCodecCtx->frame_rate_base); if (m_iCurrentFrame > m_iFrame) { av_seek_frame(m_pFormatCtx, m_VideoStream, 0); m_iCurrentFrame = -1; } } //---------------------------------------------------------------------------// // GetNextFrame // //---------------------------------------------------------------------------// void CFFMpegVideo::SetTextura(CTextura *pTextura) { TSurfaceDesc Desc; if (pTextura->Lock(NULL, Desc)) { int a = avpicture_fill ((AVPicture *)m_pFrameRGB, (unsigned char *)Desc.pBits, MPG_FORMAT, m_iWidth, m_iHeight); pTextura->Unlock(); } } //---------------------------------------------------------------------------// // GetNextFrame // //---------------------------------------------------------------------------// bool CFFMpegVideo::GetNextFrame() { // Decode packets until we have decoded a complete frame int bytesDecoded; int frameFinished; while(true) { // Work on the current packet until we have decoded all of it while(bytesRemaining > 0) { // Decode the next chunk of data bytesDecoded=avcodec_decode_video(m_pCodecCtx, m_pFrame, &frameFinished, rawData, bytesRemaining); // Was there an error? if(bytesDecoded < 0) return false; bytesRemaining-=bytesDecoded; rawData+=bytesDecoded; // Did we finish the current frame? Then we can return if(frameFinished) { m_iCurrentFrame++; return true; } } // Read the next packet, skipping all packets that aren't for this // stream do { // Free old packet if(packet.data!=NULL) av_free_packet(&packet); // Read new packet if(av_read_packet(m_pFormatCtx, &packet)<0) goto loop_exit; } while(packet.stream_index!=m_VideoStream); bytesRemaining=packet.size; rawData=packet.data; } loop_exit: // Decode the rest of the last frame bytesDecoded=avcodec_decode_video(m_pCodecCtx, m_pFrame, &frameFinished, rawData, bytesRemaining); return false; } //---------------------------------------------------------------------------// // GetFrame // //---------------------------------------------------------------------------// bool CFFMpegVideo::GetFrame(CTextura *pTextura) { bool bRes = false; if (m_iFrame == m_iCachedFrame) return true; while (m_iCurrentFrame != m_iFrame) { if (!GetNextFrame()) return false; } m_iCachedFrame = m_iFrame; // Lock de la textura y rellenado TSurfaceDesc Desc; if (pTextura->Lock(NULL, Desc)) { img_convert((AVPicture *)m_pFrameRGB, MPG_FORMAT, (AVPicture *)m_pFrame, m_pCodecCtx->pix_fmt, m_iWidth,m_iHeight); pTextura->Unlock(); bRes = true; } return bRes; } #endif