Original Post
Hello all! (Excuse me for my english level) I made a class, called "CMovieStream2" that play Video Files using FFMPEG. A basical sintax for play video is the following: Well.. in some cases.. I need to play two videos at time, but all time (and some times when I play only one video) the image show rarely, like pixeled and very ugly. I don't know how to fix this. Maybe this error is caused by the "time control" written by me in the function CMovieStream2::GrabAVIFrame(), but, I don't kow how to fix this! Any Body can help me??!! PD: If somebody know how to play video files with OpenGL if can be compiled in Windows and linux (in both), Please, Reply. I need play video files! Thanks
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#ifdef __cplusplus
}
#endif
#pragma comment( lib, "avcodec.lib")
#pragma comment( lib, "avformat.lib")
#pragma comment( lib, "swscale.lib")
#pragma comment( lib, "avutil.lib")
class CMovieStream2
{
public:
CMovieStream2(void);
bool OpenAVI(LPCTSTR szFile);
void GrabAVIFrame();
int Update(DWORD milliseconds);
public:
virtual ~CMovieStream2(void);
public:
uint8_t *data;
int width;
int height;
int nwidth;
int nheight;
DWORD dwNext;
double dNext;
double dDur;
DWORD dwDur;
DWORD dwFrame;
DWORD dwFrameSys;
void SetZeroCount(void);
void CloseAVI(void);
bool bLoad;
bool bLoop;
bool bResize;
bool bActive;
void SetResize(void);
void ClearResize(void);
private:
LPSTR chFile;
int CreateContext();
void DestroyContext();
AVFormatContext *pFormatCtx;
int videoStream;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
AVFrame *pFrame;
AVFrame *pFrameRGB;
AVPacket packet;
AVStream* video_st;
struct SwsContext *img_convert_ctx;
int frameFinished;
int numBytes;
uint8_t *buffer;
double dTimeBase;
double dPts;
};
#include "MovieStream2.h"
CMovieStream2::CMovieStream2(void)
: bLoad(false)
, bLoop(false)
, bResize(false)
{
bActive = true;
data = 0;
dwNext = 0;
dNext = 0;
width = 0;
height = 0;
nwidth = 0;
nheight = 0;
dwFrame = 0;
dwFrameSys = 0;
dPts = 0.0;
}
CMovieStream2::~CMovieStream2(void)
{
}
bool CMovieStream2::OpenAVI(LPCTSTR szFile)
{
UnicodeToAnsi(szFile, &chFile);
if(!CreateContext()) return 0;
pFrameRGB=avcodec_alloc_frame();
if(pFrameRGB==NULL)
{
printf("FFMPEG: Can't Alloc Frame! \"%s\"\n", chFile);
DestroyContext();
return 0;
}
nwidth = GetPowerOf2(pCodecCtx->width), nheight = GetPowerOf2(pCodecCtx->height);
numBytes=avpicture_get_size(PIX_FMT_RGB24, nwidth, nheight);
buffer=new uint8_t[numBytes];
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24, nwidth, nheight);
SetResize();
dwFrame=0;
dwFrameSys = 0;
dwNext=0;
dNext=0;
dPts=0;
bLoad = true;
bActive = true;
return true;
}
#define MAX_VIDEOQ_SIZE (5 * 256 * 1024)
void CMovieStream2::GrabAVIFrame()
{
if(!bActive) return;
int nDifFrame = dwFrame - dwFrameSys;
if(nDifFrame > 0)
{
for(int k = 0; k < nDifFrame; k++)
{
if(av_read_frame(pFormatCtx, &packet)<0)
{
bActive = false;
break;
}
if(k != (nDifFrame-1)) av_free_packet(&packet);
}
if(!bActive)
{
if(bLoop) SetZeroCount();
return;
}
if(packet.stream_index==videoStream)
{
dPts = 0;
// Decode video frame
avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,
packet.data, packet.size);
if(packet.dts == AV_NOPTS_VALUE
&& pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE)
{
dPts = double(*(uint64_t *)pFrame->opaque);
}
else if(packet.dts != AV_NOPTS_VALUE)
{
dPts = double(packet.dts);
}
else
{
// No se sabe el tiempo, entonces se asigna al tiempo actual
dPts = 0.0;
}
dPts *= av_q2d(video_st->time_base);
if(dPts == 0.0) dPts = dNext;
// Did we get a video frame?
if(frameFinished)
{
sws_scale (img_convert_ctx, pFrame->data, pFrame->linesize,
0, pCodecCtx->height,
pFrameRGB->data,pFrameRGB->linesize);
data = pFrameRGB->data[0];
}
}
av_free_packet(&packet);
}
}
int CMovieStream2::Update(DWORD milliseconds)
{
dwNext += milliseconds;
dNext = double(dwNext)/1000; // Convertir a segundos
dwFrame = DWORD(dNext/dTimeBase);
dwFrameSys = DWORD(dPts/dTimeBase);
return 0;
}
void CMovieStream2::SetZeroCount(void)
{
dwFrame=0;
dwFrameSys = 0;
dwNext=0;
dNext=0;
dPts=0;
DestroyContext();
CreateContext();
bActive = true;
}
void CMovieStream2::CloseAVI(void)
{
if(bLoad)
{
DestroyContext();
delete [] buffer;
if(pFrameRGB != NULL) av_free(pFrameRGB);
}
bLoad = false;
if(bResize) ClearResize();
}
void CMovieStream2::SetResize(void)
{
static int sws_flags = SWS_BICUBIC;
img_convert_ctx = sws_getContext(pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
nwidth,
nheight,
PIX_FMT_RGB24,
sws_flags, NULL, NULL, NULL);
bResize = true;
}
void CMovieStream2::ClearResize(void)
{
sws_freeContext(img_convert_ctx);
bResize = false;
}
// ** Funciones privadas
int CMovieStream2::CreateContext(void)
{
if(av_open_input_file(&pFormatCtx, chFile, NULL, 0, NULL)!=0)
{
printf("FFMPEG: Can't open file! \"%s\"\n", chFile);
return 0;
}
if(av_find_stream_info(pFormatCtx)<0)
{
printf("FFMPEG: Couldn't find stream information! \"%s\"\n", chFile);
return 0;
}
dump_format(pFormatCtx, 0, chFile, false);
videoStream=-1;
int i;
for(i=0; i<int(pFormatCtx->nb_streams); i++)
if(pFormatCtx->streams->codec->codec_type==CODEC_TYPE_VIDEO)
{
videoStream=i;
break;
}
if(videoStream==-1)
{
printf("FFMPEG: Didn't find a video stream! \"%s\"\n", chFile);
return 0;
}
// Get a pointer to the codec context for the video stream
video_st=pFormatCtx->streams[videoStream];
pCodecCtx=pFormatCtx->streams[videoStream]->codec;
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL)
{
printf("FFMPEG: Codec not found! \"%s\"\n", chFile);
return 0;
}
if(avcodec_open(pCodecCtx, pCodec)<0)
{
printf("FFMPEG: Could not open codec! \"%s\"\n", chFile);
return 0;
}
pFrame=avcodec_alloc_frame();
dTimeBase = av_q2d(video_st->time_base);
dDur = double(video_st->duration)*dTimeBase;
dwDur = DWORD(dDur*1000);
GrabAVIFrame();
return 1;
}
void CMovieStream2::DestroyContext(void)
{
av_free(pFrame);
avcodec_close(pCodecCtx);
av_close_input_file(pFormatCtx);
}
main()
{
CMovieStream2* movie = new MovieStream2();
movie->OpenAVI(_T("file.avi"));
movie->bLoop = false; // Or true
.
.
.
DWORD dwTick = GetTick();
DWORD dwTickLast = dwTick;
while(1)
{
dwTick = GetTick();
movie->Update(dwTick-dwTickLast);
movie->GrabAVIFrame();
/*Here I use movie->data for glTexImage2D
using movie->nwidth and movie->nheight as
Power-of-two dimmentions
*/
dwTickLast = dwTick;
}
}