Playing movie in texture

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
juliusctw
Posts: 392
Joined: Fri Apr 21, 2006 6:56 am
Contact:

didn't turn off mipmap

Post by juliusctw »

Oh I never disabled mip map, maybe that's causing the performance problem, my image is also 24 bit but it keeps changing to 32 bit

I think i forgot this ,
driver->setTextureCreationFlag(ETCF_ALWAYS_32_BIT, false );

i'll give this a try and let you guys know
juliusctw
Posts: 392
Joined: Fri Apr 21, 2006 6:56 am
Contact:

Here my code and my problem

Post by juliusctw »

My linux movie player

it plays fine, after i remove the mip map now it runs at 12 frames a sec, but its still too slow, the bottle neck is that i have to recreate ITexture everytime and delete ITexture every time at DumpFrame. Any suggestion on performance improvement will help greatly.

Here's the cpp file

Code: Select all

#include "IrrlichtMediaPlayer.h"

#include <ffmpeg/avcodec.h>
#include <ffmpeg/avformat.h>

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

// compile with:
// windows:
// g++ -o player IrrlichtMediaPlayer.cpp -lavformat -lavcodec -lavutil -lz -I/c/code/iamar/irrlicht-1.2/include/ -L/c/code/iamar/irrlicht-1.2/bin/Win32-gcc/ -lIrrlicht
//
// linux:
// g++ -o player IrrlichtMediaPlayer.cpp -lavformat -lavcodec -lavutil -lz -I../irrlicht/include/ -L../irrlicht -lIrrlicht -lGL -lGLU -lXxf86vm -lXext -lX11

bool IrrlichtMediaPlayer::GetNextFrame(AVFormatContext *pFormatCtx,
				       AVCodecContext *pCodecCtx, 
				       int videoStream,
				       AVFrame *pFrame)
{
    static AVPacket packet;
    static int      bytesRemaining=0;
    static uint8_t  *rawData;
    static bool     fFirstTime=true;
    int             bytesDecoded;
    int             frameFinished;

    // First time we're called, set packet.data to NULL to indicate it
    // doesn't have to be freed
    if(fFirstTime)
    {
        fFirstTime=false;
        packet.data=NULL;
    }

    // Decode packets until we have decoded a complete frame
    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(pCodecCtx, pFrame,
                &frameFinished, rawData, bytesRemaining);

            // Was there an error?
            if(bytesDecoded < 0)
            {
                fprintf(stderr, "Error while decoding frame\n");
                return false;
            }

            bytesRemaining-=bytesDecoded;
            rawData+=bytesDecoded;

            // Did we finish the current frame? Then we can return
            if(frameFinished)
                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(pFormatCtx, &packet)<0)
                goto loop_exit;
        } while(packet.stream_index!=videoStream);

        bytesRemaining=packet.size;
        rawData=packet.data;
    }

loop_exit:

    // Decode the rest of the last frame
    bytesDecoded=avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, 
        rawData, bytesRemaining);

    // Free last packet
    if(packet.data!=NULL)
        av_free_packet(&packet);

    return frameFinished!=0;
}

void IrrlichtMediaPlayer::DumpFrame(AVFrame *pFrame,
				    int width,
				    int height,
				    int iFrame)
{
    //unsigned char *buf = (unsigned char *)malloc(sizeof(unsigned char) * 3*height*width);
    static char first_time = 1;

    if(first_time)
    {
	CurrentImage = IrrVideoDriver->createImageFromData(irr::video::ECF_R8G8B8,
							   irr::core::dimension2d<irr::s32>(width, height),
							   pFrame->data[0],
							   true);
	first_time = 0;
    }

	//This is the bottle neck because I have to recreate ITexture every time
    CurrentTexture = IrrVideoDriver->addTexture("movie", CurrentImage);

    IrrVideoDriver->draw2DImage(CurrentTexture,
				irr::core::position2d<irr::s32>(0,0),
				irr::core::rect<irr::s32>(0, 0, width, height),
				0);

    IrrVideoDriver->removeTexture(CurrentTexture);
}

IrrlichtMediaPlayer::IrrlichtMediaPlayer(irr::video::IVideoDriver *irrVideoDriver, irr::ITimer *timer) 
    : IrrVideoDriver(irrVideoDriver), Timer(timer) 
{
    // Register all formats and codecs
    av_register_all();
}

IrrlichtMediaPlayer::~IrrlichtMediaPlayer() {
    // Free the RGB image
    if(Buffer != NULL)
	delete [] Buffer;
    
    if(FrameRGB != NULL)
	av_free(FrameRGB);

    // Free the YUV frame
    if(Frame != NULL)
	av_free(Frame);

    // Close the codec
    if(CodecCtx != NULL)
	avcodec_close(CodecCtx);

    // Close the video file
    if(FormatCtx != NULL)
	av_close_input_file(FormatCtx);
}

bool IrrlichtMediaPlayer::open(char *filename) {
    // Open video file
    if(av_open_input_file(&FormatCtx, filename, NULL, 0, NULL)!=0)
        return false; // Couldn't open file

    // Retrieve stream information
    if(av_find_stream_info(FormatCtx)<0)
        return false; // Couldn't find stream information

    // Dump information about file onto standard error
    dump_format(FormatCtx, 0, filename, false);

    // Find the first video stream
    VideoStream=-1;
    for(int i=0; i<FormatCtx->nb_streams; i++)
        if(FormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO)
        {
            VideoStream=i;
            break;
        }
    if(VideoStream==-1)
        return false; // Didn't find a video stream

    // Get a pointer to the codec context for the video stream
    CodecCtx=FormatCtx->streams[VideoStream]->codec;

    // Get the seconds/frame of this video stream
    SecondsPerFrame = (double)FormatCtx->streams[VideoStream]->r_frame_rate.den / FormatCtx->streams[VideoStream]->r_frame_rate.num;

    printf("seconds per frame: %f\n", SecondsPerFrame);

    // Find the decoder for the video stream
    Codec=avcodec_find_decoder(CodecCtx->codec_id);
    if(Codec==NULL)
        return false; // Codec not found

    // Inform the codec that we can handle truncated bitstreams -- i.e.,
    // bitstreams where frame boundaries can fall in the middle of packets
    if(Codec->capabilities & CODEC_CAP_TRUNCATED)
        CodecCtx->flags|=CODEC_FLAG_TRUNCATED;

    // Open codec
    if(avcodec_open(CodecCtx, Codec)<0)
        return false; // Could not open codec

    // Allocate video frame
    Frame=avcodec_alloc_frame();

    // Allocate an AVFrame structure
    FrameRGB=avcodec_alloc_frame();
    if(FrameRGB==NULL)
        return false;

    // Determine required buffer size and allocate buffer
    NumBytes=avpicture_get_size(PIX_FMT_RGB24,
				CodecCtx->width,
				CodecCtx->height);
    Buffer=new uint8_t[NumBytes];

    // Assign appropriate parts of buffer to image planes in pFrameRGB
    avpicture_fill((AVPicture *)FrameRGB, Buffer, PIX_FMT_RGB24,
		   CodecCtx->width, CodecCtx->height);

    return true;
}

bool IrrlichtMediaPlayer::play() {
    // Read frames
    static int i=0;

    if(i > 1000)
	return false;

    //if(Timer->getRealTime() - PreviousDrawTime > (SecondsPerFrame*1000))
    //{
	if(GetNextFrame(FormatCtx, CodecCtx, VideoStream, Frame))
	{
 	    img_convert((AVPicture *)FrameRGB, PIX_FMT_RGB24, (AVPicture*)Frame,
			CodecCtx->pix_fmt, CodecCtx->width, CodecCtx->height);

	    printf("dumping frame %d\n", i);

	    // Dump the frame
	    DumpFrame(FrameRGB, CodecCtx->width, CodecCtx->height, i);

	    i++;	
	}

	PreviousDrawTime = Timer->getRealTime();
    //}

    return true;
}

bool IrrlichtMediaPlayer::stop() {
    return true;
};




using namespace irr;

using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

int main(int argc, char *argv[])
{

    IrrlichtDevice *device =
	createDevice( video::EDT_OPENGL, dimension2d<s32>(800, 600), 16,
		      false, false, false, 0);

    IVideoDriver* driver = device->getVideoDriver();
    ISceneManager* smgr = device->getSceneManager();


    IrrlichtMediaPlayer imp(driver, device->getTimer());

    if(! imp.open("test_movie.avi"))
    {
		printf("problem opening movie");
		exit(1);
    }

    driver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false);
    driver->setTextureCreationFlag(ETCF_ALWAYS_32_BIT, false);
    
    while(device->run())
    {
		static int i = 0;
	
		driver->beginScene(true, true, SColor(255,100,101,140));
	
		imp.play();
	
		//smgr->drawAll();
	
		driver->endScene();
    }

    device->drop();

    return 0;
}

Here is the header file

Code: Select all

#ifndef __IRRLICHT_MEDIA_PLAYER_H__
#define __IRRLICHT_MEDIA_PLAYER_H__

// irrlicht includes
#include "irrlicht.h"

// ffmpeg includes
#include <ffmpeg/avcodec.h>
#include <ffmpeg/avformat.h>

class IrrlichtMediaPlayer {
private:
    irr::ITimer *Timer;
    irr::video::IVideoDriver *IrrVideoDriver;
    irr::video::IImage *CurrentImage;
    irr::video::ITexture *CurrentTexture;

    unsigned long PreviousDrawTime;

    double SecondsPerFrame;

    AVFormatContext *FormatCtx;
    int             VideoStream;
    AVCodecContext  *CodecCtx;
    AVCodec         *Codec;
    AVFrame         *Frame; 
    AVFrame         *FrameRGB;
    int             NumBytes;
    uint8_t         *Buffer;

    bool GetNextFrame(AVFormatContext *pFormatCtx,
		      AVCodecContext *pCodecCtx, 
		      int videoStream,
		      AVFrame *pFrame);

    void DumpFrame(AVFrame *pFrame,
		   int width,
		   int height,
		   int iFrame);
    
public:
    // constructor
    IrrlichtMediaPlayer(irr::video::IVideoDriver *irrVideoDriver,
			irr::ITimer *timer);

    // destructor
    ~IrrlichtMediaPlayer();

    // functions to open, play, and stop the media
    bool open(char *filename);
    bool play();
    // make a "close" function
    bool stop();
};

#endif
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

Just as I said: Use only one (permanent) texture and make use of lock and unlock.
juliusctw
Posts: 392
Joined: Fri Apr 21, 2006 6:56 am
Contact:

finally profiled the problem

Post by juliusctw »

ok after digging into the source code, I found out that each time I create a new ITexture, I was calling copyToScaling which is probably the slowest algorithm inside irrlicht.

Hybrid suggested that I should just create a single ITexture, use lock to get the pointer to the data, and then write the new IImage into that buffer. I have been trying to do that the whole time,,,,, I knew that already....

the problem for me is that it seems to me that regardless, I would still have to re Scale the image every frame b/c the movie is not in optimum power of two. So does that mean that every single frame, I would have to scale the frame before writing to the buffer.


But I don't think that will provide as much performance boost as expected since I am still scaling the image. Is there a way which I don't scale the image???

or perhaps I should write my own faster scaling function? Emil how did you deal with non power of 2 movies????

I tried to read copyToScaling inside CImage, but i don't really understand it, i think it scales the image to the power of two? is that the case??? Or does it maintain the original size and writes transparent bytes to the rest of them to fill up the buffer?


Why do we even need to scale the image in the first place? Does openGL "requires" power of two images?
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

You have to set up a texture which is at least as large as your movie and then copy line by line, with proper padding of each line (i.e. skip the rest of each line). Then use texture coords which use only the part of the texture which shows the movie.
juliusctw
Posts: 392
Joined: Fri Apr 21, 2006 6:56 am
Contact:

alright i'll do that

Post by juliusctw »

alright, i'll give that a try and report back when i'm done , thanks
juliusctw
Posts: 392
Joined: Fri Apr 21, 2006 6:56 am
Contact:

i did it

Post by juliusctw »

Ok , i followed your instruction, and now i have fps to 21 fps, it is still kinda slow, but now my major concern is that Irrlicht for some reason scales the movie to a different size, are you familiar with that?
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

You could profile your app to see what is happening, or simply debug.
Last edited by hybrid on Tue Jan 23, 2007 12:01 pm, edited 1 time in total.
juliusctw
Posts: 392
Joined: Fri Apr 21, 2006 6:56 am
Contact:

ok, i got it

Post by juliusctw »

Ok
quick update
I finally got the speed I want, but now I have decode problem, some of the frames are badly decoded, mpeg2 works fine, but xvid with frames that change quicks are messed up, i'll fix that next
juliusctw
Posts: 392
Joined: Fri Apr 21, 2006 6:56 am
Contact:

i am so stuck

Post by juliusctw »

does anybody have experience with libav ?? I am very stuck ,

everything seems fine, but some times, the libav load frame function won't read the entire frame? anybody?
juliusctw
Posts: 392
Joined: Fri Apr 21, 2006 6:56 am
Contact:

It all works now

Post by juliusctw »

Great news

The linux movie player all works now, it plays divx, xvid, ogg, basically everything FFMPEG. I actually wrote 2 versions, one works in linux and the other on windows as well, sorry apple people :(

I'll write a cleaner version and post it later.
Emil_halim
Posts: 518
Joined: Tue Mar 29, 2005 9:02 pm
Location: Alex,Egypt
Contact:

Post by Emil_halim »

that is a good news . :D

i want top tell you some thing about the speed of movie, with my Nvidia 5200FX , the lock and unlock and writing to texture under DirctX9 is much faster then OpenGL when playing the same movie in each driver.

any way , Keep up your good work.
hansel
Posts: 2
Joined: Tue Mar 13, 2007 3:32 pm

Post by hansel »

Ey :D

I'm triying to improve the Linux movie player, now it's using lock and unlock functions instead of creating a texture every frame. The framerate playing a 640x480 Divx 5 movie is now 350 fps using a nvidia 7300 Gs, I think it's a real improvement compared to the past :mrgreen:



I just need help to create the pause (not really, It's just to stop playing xDD), stop, replay (loop) functions and getting some stability with Ati cards. Someone is interesed? :D



videoPlayer.h

Code: Select all

#ifndef __VIDEO_PLAYER_H__
#define __VIDEO_PLAYER_H__

// irrlicht includes
#include "irrlicht.h"

// ffmpeg includes
#include <ffmpeg/avcodec.h>
#include <ffmpeg/avformat.h>


using namespace irr;





class cVideoPlayer
{
private:
    irr::ITimer *Timer;
    irr::video::IVideoDriver *IrrVideoDriver;
    irr::video::IImage *CurrentImage;
    irr::video::ITexture *CurrentTexture;

    unsigned long lastTime;

    double SecondsPerFrame;

    AVFormatContext *FormatCtx;
    int             VideoStream;
    AVCodecContext  *CodecCtx;
    AVCodec         *Codec;
    AVFrame         *Frame;
    AVFrame         *FrameRGB;
    int             NumBytes;
    uint8_t         *Buffer;

    bool GetNextFrame(AVFormatContext *pFormatCtx,
                      AVCodecContext *pCodecCtx,
                      int videoStream,
                      AVFrame *pFrame);

    bool DumpFrame(AVFrame *pFrame, int width, int height);

    s32* p;
    s32* pimage;

public:
    // constructor
    cVideoPlayer(irr::video::IVideoDriver *irrVideoDriver,
                 irr::ITimer *timer);
    cVideoPlayer(irr::video::IVideoDriver *irrVideoDriver,
                 irr::ITimer *timer, char* filename);

    // destructor
    ~cVideoPlayer();

    // functions to open, play, and stop the media
    bool open(char *filename);
    bool play();
    // make a "close" function
    bool stop();

    irr::video::ITexture* getVideoTexture();

    void drawVideoTexture();
};

#endif

videoPlayer.cpp

Code: Select all

#include "videoPlayer.h"

#include <ffmpeg/avcodec.h>
#include <ffmpeg/avformat.h>

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>


using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------
// Original code by: juliusctw
// Modified by Hansel

// compile with:
// windows:
// g++ -o player cVideoPlayer.cpp -lavformat -lavcodec -lavutil -lz -I/c/code/iamar/irrlicht-1.2/include/ -L/c/code/iamar/irrlicht-1.2/bin/Win32-gcc/ -lIrrlicht
//
// linux:
// g++ -o player cVideoPlayer.cpp -lavformat -lavcodec -lavutil -lz -I../irrlicht/include/ -L../irrlicht -lIrrlicht -lGL -lGLU -lXxf86vm -lXext -lX11


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------

cVideoPlayer::cVideoPlayer(irr::video::IVideoDriver *irrVideoDriver, irr::ITimer *timer)
        : IrrVideoDriver(irrVideoDriver), Timer(timer)
{
    IrrVideoDriver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false);
    IrrVideoDriver->setTextureCreationFlag(ETCF_ALWAYS_32_BIT, true);
    // Register all formats and codecs
    av_register_all();
}


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------

cVideoPlayer::cVideoPlayer(irr::video::IVideoDriver *irrVideoDriver, irr::ITimer *timer, char* filename)
        : IrrVideoDriver(irrVideoDriver), Timer(timer)
{
    IrrVideoDriver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false);
    IrrVideoDriver->setTextureCreationFlag(ETCF_ALWAYS_32_BIT, true);
    // Register all formats and codecs
    av_register_all();

    if (! open(filename))
    {
        printf("problem opening movie");
        exit(1);
    }
}


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------


cVideoPlayer::~cVideoPlayer()
{
    // Free the RGB image
    if (Buffer != NULL)
        delete [] Buffer;

    if (FrameRGB != NULL)
        av_free(FrameRGB);

    // Free the YUV frame
    if (Frame != NULL)
        av_free(Frame);

    // Close the codec
    if (CodecCtx != NULL)
        avcodec_close(CodecCtx);

    // Close the video file
    if (FormatCtx != NULL)
        av_close_input_file(FormatCtx);
}


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------



bool cVideoPlayer::open(char *filename)
{
    // Open video file
    if (av_open_input_file(&FormatCtx, filename, NULL, 0, NULL)!=0)
        return false; // Couldn't open file

    // Retrieve stream information
    if (av_find_stream_info(FormatCtx)<0)
        return false; // Couldn't find stream information

    // Dump information about file onto standard error
    dump_format(FormatCtx, 0, filename, false);

    // Find the first video stream
    VideoStream=-1;
    for (int i=0; i<FormatCtx->nb_streams; i++)
        if (FormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO)
        {
            VideoStream=i;
            break;
        }
    if (VideoStream==-1)
        return false; // Didn't find a video stream

    // Get a pointer to the codec context for the video stream
    CodecCtx=FormatCtx->streams[VideoStream]->codec;

    // Get the seconds/frame of this video stream
    SecondsPerFrame = (double)FormatCtx->streams[VideoStream]->r_frame_rate.den / FormatCtx->streams[VideoStream]->r_frame_rate.num;
    printf("Duration:  %f", (double)FormatCtx->duration);

    printf("  seconds per frame: %f\n", SecondsPerFrame);

    // Find the decoder for the video stream
    Codec=avcodec_find_decoder(CodecCtx->codec_id);
    if (Codec==NULL)
        return false; // Codec not found

    // Inform the codec that we can handle truncated bitstreams -- i.e.,
    // bitstreams where frame boundaries can fall in the middle of packets
    if (Codec->capabilities & CODEC_CAP_TRUNCATED)
        CodecCtx->flags|=CODEC_FLAG_TRUNCATED;

    // Open codec
    if (avcodec_open(CodecCtx, Codec)<0)
        return false; // Could not open codec

    // Allocate video frame
    Frame=avcodec_alloc_frame();

    // Allocate an AVFrame structure
    FrameRGB=avcodec_alloc_frame();
    if (FrameRGB==NULL)
        return false;

    // Determine required buffer size and allocate buffer
    NumBytes=avpicture_get_size(PIX_FMT_RGB32,
                                CodecCtx->width,
                                CodecCtx->height);
    Buffer=new uint8_t[NumBytes];

    // Assign appropriate parts of buffer to image planes in pFrameRGB
    avpicture_fill((AVPicture *)FrameRGB, Buffer, PIX_FMT_RGB32,
                   CodecCtx->width, CodecCtx->height);

    return true;
}



//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------



bool cVideoPlayer::play()
{
    // Read frames
    static int i=0;

    if (i > 1000000) return false;

    if (Timer->getRealTime() - lastTime > (SecondsPerFrame*1000))
    {
        if (GetNextFrame(FormatCtx, CodecCtx, VideoStream, Frame))
        {
            img_convert((AVPicture *)FrameRGB, PIX_FMT_RGB32, (AVPicture*)Frame,
                        CodecCtx->pix_fmt, CodecCtx->width, CodecCtx->height);

            printf("dumping frame %d\n", i);

            // Dump the frame
            DumpFrame(FrameRGB, CodecCtx->width, CodecCtx->height);

            i++;
        }

        lastTime = Timer->getRealTime();
    }

    return true;
}


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------


bool cVideoPlayer::stop()
{
    return true;
};


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------


bool cVideoPlayer::GetNextFrame(AVFormatContext *pFormatCtx,
                                AVCodecContext *pCodecCtx,
                                int videoStream,
                                AVFrame *pFrame)
{
    static AVPacket packet;
    static int      bytesRemaining=0;
    static uint8_t  *rawData;
    static bool     fFirstTime=true;
    int             bytesDecoded;
    int             frameFinished;


    // First time we're called, set packet.data to NULL to indicate it
    // doesn't have to be freed
    if (fFirstTime)
    {
        fFirstTime=false;
        packet.data=NULL;
    }

    // Decode packets until we have decoded a complete frame
    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(pCodecCtx, pFrame,
                                              &frameFinished, rawData, bytesRemaining);

            // Was there an error?
            if (bytesDecoded < 0)
            {
                fprintf(stderr, "Error while decoding frame\n");
                return false;
            }

            bytesRemaining-=bytesDecoded;
            rawData+=bytesDecoded;

            // Did we finish the current frame? Then we can return
            if (frameFinished)
                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(pFormatCtx, &packet)<0)
                goto loop_exit;
        }
        while (packet.stream_index!=videoStream);

        bytesRemaining=packet.size;
        rawData=packet.data;
    }

loop_exit:

    // Decode the rest of the last frame
    bytesDecoded=avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,
                                      rawData, bytesRemaining);

    // Free last packet
    if (packet.data!=NULL)
        av_free_packet(&packet);

    return frameFinished!=0;
}



//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------



bool cVideoPlayer::DumpFrame(AVFrame *pFrame,
                             int width,
                             int height)
{
    //unsigned char *buf = (unsigned char *)malloc(sizeof(unsigned char) * 3*height*width);
    static char first_time = 1;

    if (first_time)
    {
        CurrentImage = IrrVideoDriver->createImageFromData(irr::video::ECF_A8R8G8B8,
                       irr::core::dimension2d<irr::s32>(width, height),
                       pFrame->data[0],
                       true);
        first_time = 0;
        CurrentTexture = IrrVideoDriver->addTexture("movie", CurrentImage);
    }

    p = (s32*)CurrentTexture->lock ();
    pimage = (s32*)CurrentImage->lock ();


    for (int i = 0; i < width*height; i++) p[i] = pimage[i];

    // unlock de texture and the image
    CurrentTexture->unlock();
    CurrentImage->unlock();



    return true;

}


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------

irr::video::ITexture* cVideoPlayer::getVideoTexture()
{
    return CurrentTexture;
}



//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------



void cVideoPlayer::drawVideoTexture()
{
    // draw the texture
    IrrVideoDriver->draw2DImage(CurrentTexture, irr::core::position2d<irr::s32>(0,0),
                                irr::core::rect<irr::s32>(0,0,1024,768), 0, irr::video::SColor(255,255,255,255), true);
}



//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------

main.cpp

Code: Select all

#include "videoPlayer.h"
#include <stdio.h>


using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;


using namespace irr;



//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------

IrrlichtDevice *device;
IVideoDriver* driver;
ISceneManager* smgr;




class MyEventReceiver : public IEventReceiver
{
public:
    virtual bool OnEvent(SEvent event)
    {
        /*
        If the key 'W' or 'S' was left up, we get the position of the scene node,
        and modify the Y coordinate a little bit. So if you press 'W', the node
        moves up, and if you press 'S' it moves down.
        */

        if (event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown)
        {
            switch (event.KeyInput.Key)
            {
            case KEY_KEY_A:
                printf("-> Key A\n");
                break;

                return true;
            }
        }

        return false;
    }
};


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------


int main(int argc, char *argv[])
{
    MyEventReceiver receiver;

    device = createDevice( video::EDT_OPENGL, dimension2d<s32>(640, 480), 32, false, false, false, &receiver);

    driver = device->getVideoDriver();
    smgr = device->getSceneManager();

    // initialise the videoPlayer with a filename
    cVideoPlayer* videoPlayer = new cVideoPlayer(driver, device->getTimer(), "Futurama.avi");


    // to get the framerate (fps)
    int lastFPS = -1;

    while (device->run())
    {
        static int i = 0;

        driver->beginScene(true, true, SColor(255,100,101,140));

        // play the video
        videoPlayer->play();

        // draw the texture in a rectangle
        videoPlayer->drawVideoTexture();

        //smgr->drawAll();

        driver->endScene();


        // show the framerate (fps)
        int fps = driver->getFPS();
        if (lastFPS != fps)
        {
            wchar_t tmp[1024];
            swprintf(tmp, 1024, L"Video Example - Irrlicht Engine [%ls] fps: %d",
                     driver->getName(), fps);

            device->setWindowCaption(tmp);
            lastFPS = fps;
        }
    }

    device->drop();

    return 0;
}



//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------

pd.- My intention is to develop a multiplatform and multiengine (Irrlicht and Ogre) video library :wink:
monkeycracks
Posts: 1029
Joined: Thu Apr 06, 2006 12:45 am
Location: Tennesee, USA
Contact:

Post by monkeycracks »

Could you include a movie file(doesn't have to be big) and a binary? I'll test it with my ATI Radeon 9650 if you want, but I don't have time to code+compile+get a movie file ;)
hansel
Posts: 2
Joined: Tue Mar 13, 2007 3:32 pm

Post by hansel »

monkeycracks wrote:Could you include a movie file(doesn't have to be big) and a binary? I'll test it with my ATI Radeon 9650 if you want, but I don't have time to code+compile+get a movie file ;)

Sorry but in this PC I only have 2 movies, one is private and the other is too large (120 Mb) :(



I have added new functions: pause, goToFrame, goToSecond, stop, restart, getFrameRate, getVideoTexture and setLoop.




videoPlayer.h

Code: Select all

#ifndef __VIDEO_PLAYER_H__
#define __VIDEO_PLAYER_H__

// irrlicht includes
#include "irrlicht.h"

// ffmpeg includes
#include <ffmpeg/avcodec.h>
#include <ffmpeg/avformat.h>


using namespace irr;





class cVideoPlayer
{
private:

    irr::ITimer *Timer;
    irr::video::IVideoDriver *IrrVideoDriver;
    irr::video::IImage *CurrentImage;
    irr::video::ITexture *CurrentTexture;

    unsigned long lastTime;

    double SecondsPerFrame;

    AVFormatContext *FormatCtx;
    int             VideoStream;
    AVCodecContext  *CodecCtx;
    AVCodec         *Codec;
    AVFrame         *Frame;
    AVFrame         *FrameRGB;
    int             NumBytes;
    uint8_t         *Buffer;

    bool GetNextFrame(AVFormatContext *pFormatCtx,
                      AVCodecContext *pCodecCtx,
                      int videoStream,
                      AVFrame *pFrame);

    bool DumpFrame(AVFrame *pFrame, int width, int height);

    s32* p;
    s32* pimage;

    enum cState {playing, paused, stopped, manual};
    cState state;
    bool streamOpen;
    char* actualFilename;
    int actualFrame;
    bool loop;
    int replayCont;
    float framerate;
    int seekFactor;

public:
    // constructors
    cVideoPlayer(irr::video::IVideoDriver *irrVideoDriver,
                 irr::ITimer *timer);
    cVideoPlayer(irr::video::IVideoDriver *irrVideoDriver,
                 irr::ITimer *timer, char* filename);

    // destructor
    ~cVideoPlayer();

    // functions to open, play, and stop the media
    bool open(char *filename);

    // refresh the video
    bool refresh();

    // play the video
    bool play();

    // stop the video
    bool stop();

    // pause the video
    bool pause();

    // get the video texture
    irr::video::ITexture* getVideoTexture();

    // draw the video texture
    void drawVideoTexture();

    // close the video stream
    bool restartStream();

    // set looped
    void setLoop(bool l);

    // go to selected second
    void goToSecond(int numSecond);

    // get selected frame
    bool goToFrame(int numFrame);

    // get the movie framerate
    float getFrameRate();

    // get the seek factor (15 by default)
    int getSeekFactor();

    // set the seek factor
    void setSeekFactor(int sf);
};

#endif

videoPlayer.cpp

Code: Select all

#include "videoPlayer.h"

#include <ffmpeg/avcodec.h>
#include <ffmpeg/avformat.h>

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>


using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------
// Original code by: juliusctw
// Modified by Hansel

// compile with:
// windows:
// g++ -o player cVideoPlayer.cpp -lavformat -lavcodec -lavutil -lz -I/c/code/iamar/irrlicht-1.2/include/ -L/c/code/iamar/irrlicht-1.2/bin/Win32-gcc/ -lIrrlicht
//
// linux:
// g++ -o player cVideoPlayer.cpp -lavformat -lavcodec -lavutil -lz -I../irrlicht/include/ -L../irrlicht -lIrrlicht -lGL -lGLU -lXxf86vm -lXext -lX11


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------

cVideoPlayer::cVideoPlayer(irr::video::IVideoDriver *irrVideoDriver, irr::ITimer *timer)
        : IrrVideoDriver(irrVideoDriver), Timer(timer)
{
    IrrVideoDriver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false);
    IrrVideoDriver->setTextureCreationFlag(ETCF_ALWAYS_32_BIT, true);
    // Register all formats and codecs
    av_register_all();

    state = stopped;
    streamOpen = false;
    actualFrame = 0;
    loop = false;
    replayCont = 0;
    seekFactor = 15;
}


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------

cVideoPlayer::cVideoPlayer(irr::video::IVideoDriver *irrVideoDriver, irr::ITimer *timer, char* filename)
        : IrrVideoDriver(irrVideoDriver), Timer(timer)
{
    IrrVideoDriver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false);
    IrrVideoDriver->setTextureCreationFlag(ETCF_ALWAYS_32_BIT, true);
    // Register all formats and codecs
    av_register_all();

    if (! open(filename))
    {
        printf("problem opening movie");
        exit(1);
    }


    state = stopped;
    streamOpen = false;
    actualFrame = 0;
    loop = false;
    replayCont = 0;
    seekFactor = 15;
}


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------


cVideoPlayer::~cVideoPlayer()
{
    // Free the RGB image
    if (Buffer != NULL)
        delete [] Buffer;

    if (FrameRGB != NULL)
        av_free(FrameRGB);

    // Free the YUV frame
    if (Frame != NULL)
        av_free(Frame);

    // Close the codec
    if (CodecCtx != NULL)
        avcodec_close(CodecCtx);

    // Close the video file
    if (FormatCtx != NULL)
        av_close_input_file(FormatCtx);
}


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------



bool cVideoPlayer::open(char *filename)
{
    actualFilename = filename;

    // Open video file
    if (av_open_input_file(&FormatCtx, filename, NULL, 0, NULL)!=0)
        return false; // Couldn't open file

    // Retrieve stream information
    if (av_find_stream_info(FormatCtx)<0)
        return false; // Couldn't find stream information

    // Dump information about file onto standard error
    dump_format(FormatCtx, 0, filename, false);

    // Find the first video stream
    VideoStream=-1;
    for (int i=0; i<FormatCtx->nb_streams; i++)
        if (FormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO)
        {
            VideoStream=i;
            break;
        }
    if (VideoStream==-1)
        return false; // Didn't find a video stream

    // Get a pointer to the codec context for the video stream
    CodecCtx=FormatCtx->streams[VideoStream]->codec;

    // Get the seconds/frame of this video stream
    SecondsPerFrame = (double)FormatCtx->streams[VideoStream]->r_frame_rate.den / FormatCtx->streams[VideoStream]->r_frame_rate.num;
    printf("Duration:  %f", (double)FormatCtx->duration);

    printf("  seconds per frame: %f\n", SecondsPerFrame);

    // Find the decoder for the video stream
    Codec=avcodec_find_decoder(CodecCtx->codec_id);
    if (Codec==NULL)
        return false; // Codec not found

    // Inform the codec that we can handle truncated bitstreams -- i.e.,
    // bitstreams where frame boundaries can fall in the middle of packets
    if (Codec->capabilities & CODEC_CAP_TRUNCATED)
        CodecCtx->flags|=CODEC_FLAG_TRUNCATED;

    // Open codec
    if (avcodec_open(CodecCtx, Codec)<0)
        return false; // Could not open codec

    // Allocate video frame
    Frame=avcodec_alloc_frame();

    // Allocate an AVFrame structure
    FrameRGB=avcodec_alloc_frame();
    if (FrameRGB==NULL)
        return false;

    // Determine required buffer size and allocate buffer
    NumBytes=avpicture_get_size(PIX_FMT_RGB32,
                                CodecCtx->width,
                                CodecCtx->height);
    Buffer=new uint8_t[NumBytes];

    // Assign appropriate parts of buffer to image planes in pFrameRGB
    avpicture_fill((AVPicture *)FrameRGB, Buffer, PIX_FMT_RGB32,
                   CodecCtx->width, CodecCtx->height);


    // get the movie framerate
    framerate = FormatCtx->streams[VideoStream]->r_frame_rate.num;

    return true;
}



//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------


bool cVideoPlayer::refresh()
{
    if (state == playing)
    {
        if (Timer->getRealTime() - lastTime > (SecondsPerFrame*1000))
        {
            if (GetNextFrame(FormatCtx, CodecCtx, VideoStream, Frame))
            {
                img_convert((AVPicture *)FrameRGB, PIX_FMT_RGB32, (AVPicture*)Frame,
                            CodecCtx->pix_fmt, CodecCtx->width, CodecCtx->height);

                printf("Replay Num.: %d  ::  Dumping Frame: %d  ::  FrameRate: %f\n", replayCont, actualFrame, framerate);

                // Dump the frame
                DumpFrame(FrameRGB, CodecCtx->width, CodecCtx->height);

                actualFrame++;
            }
            else
            {
                state = stopped;
                printf("->End\n");
            }

            lastTime = Timer->getRealTime();
        }
    }

    if (state == stopped)
    {
        actualFrame = 0;
        restartStream();

        // if looped then replay the movie
        if (loop)
        {
            replayCont ++;
            state = playing;
            streamOpen = true;
        }
    }
}


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------



bool cVideoPlayer::play()
{
    if (state == stopped) replayCont++;
    state = playing;
    streamOpen = true;

    return true;
}


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------


bool cVideoPlayer::stop()
{
    state = stopped;

    return true;
};


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------

bool cVideoPlayer::pause()
{
    state = paused;

    return true;
};


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------


bool cVideoPlayer::GetNextFrame(AVFormatContext *pFormatCtx,
                                AVCodecContext *pCodecCtx,
                                int videoStream,
                                AVFrame *pFrame)
{
    static AVPacket packet;
    static int      bytesRemaining=0;
    static uint8_t  *rawData;
    static bool     fFirstTime=true;
    int             bytesDecoded;
    int             frameFinished;


    // First time we're called, set packet.data to NULL to indicate it
    // doesn't have to be freed
    if (fFirstTime)
    {
        fFirstTime=false;
        packet.data=NULL;
    }

    // Decode packets until we have decoded a complete frame
    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(pCodecCtx, pFrame,
                                              &frameFinished, rawData, bytesRemaining);

            // Was there an error?
            if (bytesDecoded < 0)
            {
                fprintf(stderr, "Error while decoding frame\n");
                return false;
            }

            bytesRemaining-=bytesDecoded;
            rawData+=bytesDecoded;

            // Did we finish the current frame? Then we can return
            if (frameFinished)
                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(pFormatCtx, &packet)<0)
                goto loop_exit;
        }
        while (packet.stream_index!=videoStream);

        bytesRemaining=packet.size;
        rawData=packet.data;
    }

loop_exit:

    // Decode the rest of the last frame
    bytesDecoded=avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,
                                      rawData, bytesRemaining);

    // Free last packet
    if (packet.data!=NULL)
        av_free_packet(&packet);

    return frameFinished!=0;
}



//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------



bool cVideoPlayer::DumpFrame(AVFrame *pFrame,
                             int width,
                             int height)
{
    static char first_time = 1;

    if (first_time)
    {
        CurrentImage = IrrVideoDriver->createImageFromData(irr::video::ECF_A8R8G8B8,
                       irr::core::dimension2d<irr::s32>(width, height),
                       pFrame->data[0],
                       true);
        first_time = 0;
        CurrentTexture = IrrVideoDriver->addTexture("movie", CurrentImage);
    }

    p = (s32*)CurrentTexture->lock ();
    pimage = (s32*)CurrentImage->lock ();


    for (int i = 0; i < width*height; i++) p[i] = pimage[i];

    // unlock de texture and the image
    CurrentTexture->unlock();
    CurrentImage->unlock();



    return true;

}


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------

irr::video::ITexture* cVideoPlayer::getVideoTexture()
{
    return CurrentTexture;
}



//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------



void cVideoPlayer::drawVideoTexture()
{
    // draw the texture
    IrrVideoDriver->draw2DImage(CurrentTexture, irr::core::position2d<irr::s32>(0,0),
                                irr::core::rect<irr::s32>(0,0,1024,768), 0, irr::video::SColor(255,255,255,255), true);
}



//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------


bool cVideoPlayer::restartStream()
{
    //Close stream
    if ( FormatCtx && streamOpen)
    {
        streamOpen = false;

        if (av_open_input_file(&FormatCtx, actualFilename, NULL, 0, NULL)!=0) return false; // Couldn't open file
    }
}


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------


void cVideoPlayer::setLoop(bool l)
{
    loop = l;
}


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------


void cVideoPlayer::goToSecond(int numSecond)
{
    restartStream();

    state = manual;


    goToFrame((int)framerate*numSecond);
}


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------




bool cVideoPlayer::goToFrame(int numFrame)
{
    // seek the movie
    if (numFrame < seekFactor) av_seek_frame(FormatCtx, VideoStream, numFrame, AVSEEK_FLAG_ANY);
    else
    {
        av_seek_frame(FormatCtx, VideoStream, numFrame-seekFactor, AVSEEK_FLAG_ANY);

        // advance to the real selected frame
        for (int i = 0; i < seekFactor; i++) GetNextFrame(FormatCtx, CodecCtx, VideoStream, Frame);
    }

    actualFrame = numFrame;

    printf("Replay Num.: %d  ::  Dumping Frame: %d  ::  FrameRate: %f\n", replayCont, actualFrame, framerate);

    return true;
}



//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------


float cVideoPlayer::getFrameRate()
{
    return framerate;
}


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------


main.cpp

Code: Select all

#include "videoPlayer.h"
#include <stdio.h>


using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;


using namespace irr;



//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------

IrrlichtDevice *device;
IVideoDriver* driver;
ISceneManager* smgr;

cVideoPlayer* videoPlayer;


class MyEventReceiver : public IEventReceiver
{
public:
    virtual bool OnEvent(SEvent event)
    {
        /*
        If the key 'W' or 'S' was left up, we get the position of the scene node,
        and modify the Y coordinate a little bit. So if you press 'W', the node
        moves up, and if you press 'S' it moves down.
        */

        if (event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown)
        {
            switch (event.KeyInput.Key)
            {
            case KEY_KEY_A: videoPlayer->play();
                printf("-> Play\n");
                break;

            case KEY_KEY_S: videoPlayer->pause();
                printf("-> Pause\n");
                break;

            case KEY_KEY_D: videoPlayer->stop();
                printf("-> Stop\n");
                break;

                return true;

                 case KEY_KEY_F: videoPlayer->goToSecond(20);
                printf("-> Manual\n");
                break;

                return true;

            }
        }

        return false;
    }
};


//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------


int main(int argc, char *argv[])
{
    MyEventReceiver receiver;

    device = createDevice( video::EDT_OPENGL, dimension2d<s32>(640, 480), 32, false, false, false, &receiver);

    driver = device->getVideoDriver();
    smgr = device->getSceneManager();

    // initialise the videoPlayer with a filename
    videoPlayer = new cVideoPlayer(driver, device->getTimer(), "Futurama.avi");
    videoPlayer->setLoop(false);

    // to get the framerate (fps)
    int lastFPS = -1;

    while (device->run())
    {
        static int i = 0;

        driver->beginScene(true, true, SColor(255,100,101,140));

        // refresh the video
        videoPlayer->refresh();

        // draw the texture in a rectangle
        videoPlayer->drawVideoTexture();

        //smgr->drawAll();

        driver->endScene();


        // show the framerate (fps)
        int fps = driver->getFPS();
        if (lastFPS != fps)
        {
            wchar_t tmp[1024];
            swprintf(tmp, 1024, L"Video Example - Irrlicht Engine [%ls] fps: %d",
                     driver->getName(), fps);

            device->setWindowCaption(tmp);
            lastFPS = fps;
        }
    }

    device->drop();

    return 0;
}



//-----------------------------------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------------------------------

;)
Post Reply