Description:

Just provides an object oriented way to allow video to play in SFML using ffmpeg.


Thanks To:


Changelog:


Installation and usage:

To use, you must have avcodec, avformat and swscale installed. You'll probably have to provide the correct path to .h files. Also, you need to link against avcodec, avformat and swscale. If using Ubuntu or any of its variants, just do this:

sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev

In windows, you have to download the shared-dev version for win32/win64 here: http://ffmpeg.arrozcru.org/autobuilds/

You use the Video class just as you would use sf::Image, except that you have to manually call the Update function each loop.

This is better explained by an example: (main.cpp):

#include <iostream>
#include "video.hpp"
 
int main()
{
    Video viddy("myVideo.mkv");
    sf::Sprite sprite(viddy);
 
    int w = viddy.GetWidth();
    int h = viddy.GetHeight();
 
    sf::RenderWindow App(sf::VideoMode(w, h), "Video with SFML");
 
    sf::Clock clock;
 
    bool Running = true;
    while (Running)
    {
        float time = clock.GetElapsedTime();
        clock.Reset();
 
        viddy.Update(time);
 
        App.Clear();
 
        App.Draw(sprite);
 
        App.Display();
 
        sf::Event Event;
        while (App.GetEvent(Event))
        {
            if (Event.Type == sf::Event::Closed)
                Running = false;
 
            if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape))
                Running = false;
 
        }
 
    }
 
    App.Close();
 
    return EXIT_SUCCESS;
}

Here is video.hpp:

#ifndef VIDEO_H
#define VIDEO_H
 
#include <SFML/Graphics.hpp>
#include <iostream>
#include <string>
 
using namespace std;
 
typedef unsigned int nuint;
 
#define __STDC_CONSTANT_MACROS
extern "C"
{
    #include <libavformat/avformat.h>
    #include <libavcodec/avcodec.h>
    #include <libswscale/swscale.h>
}
 
class Video
{
 
private:
//Variables
    sf::Image       m_Image;
    bool            m_bVideoLoaded;
    bool            m_bImageNeedsUpdate;
    AVFormatContext *m_pFormatCtx;
    sf::Uint8       m_iVideoStream;
    sf::Uint32      m_iFrameSize;
    AVCodecContext  *m_pCodecCtx;
    AVFrame         *m_pFrame;
    AVFrame         *m_pFrameRGB;
    sf::Uint8       *m_pBuffer;
    AVPacket        m_Packet;
    AVCodec         *m_pCodec;
    SwsContext      *img_convert_ctx;
    string          m_sFilename;
    float           m_fSecondsPerFrame;
    float           m_fTimePassedSinceLastFrameUpdate;
 
//Functions
    void UpdateImage();
    void LoadNextFrame();
public:
    Video();
    explicit Video(const string& filename = "");
    bool LoadFromFile(const string& filename);
    void Update(float time);
 
    sf::Vector2i Size() const { return sf::Vector2i(GetWidth(), GetHeight()); }
 
    nuint GetWidth() const { return m_pCodecCtx->width; }
    nuint GetHeight() const { return m_pCodecCtx->height; }
 
    float GetFrameRate() const { return 1/m_fSecondsPerFrame; }
 
    operator const sf::Image&() const {return m_Image;}
    ~Video();
 
    sf::Color GetPixel(nuint x, nuint y) const;
 
    void CloseVideo();
 
private:
    Video(const Video& rhs);
    Video& operator=(const Video& rhs);
};
 
#endif // VIDEO_H

And here is video.cpp:

#include "video.hpp"
 
Video::Video() :    m_Image(),
                    m_bVideoLoaded(false),
                    m_bImageNeedsUpdate(false),
                    m_pFormatCtx(NULL),
                    m_iVideoStream(0),
                    m_iFrameSize(0),
                    m_pCodecCtx(NULL),
                    m_pFrame(NULL),
                    m_pFrameRGB(NULL),
                    m_pBuffer(NULL),
                    m_Packet(),
                    m_pCodec(NULL),
                    img_convert_ctx(NULL),
                    m_sFilename(""),
                    m_fSecondsPerFrame(0),
                    m_fTimePassedSinceLastFrameUpdate(0)
{
    av_register_all();
}
 
 
Video::Video(const string& filename) :  m_Image(),
                                        m_bVideoLoaded(false),
                                        m_bImageNeedsUpdate(false),
                                        m_pFormatCtx(NULL),
                                        m_iVideoStream(0),
                                        m_iFrameSize(0),
                                        m_pCodecCtx(NULL),
                                        m_pFrame(NULL),
                                        m_pFrameRGB(NULL),
                                        m_pBuffer(NULL),
                                        m_Packet(),
                                        m_pCodec(NULL),
                                        img_convert_ctx(NULL),
                                        m_sFilename(""),
                                        m_fSecondsPerFrame(0),
                                        m_fTimePassedSinceLastFrameUpdate(0)
{
    av_register_all();
    if (!filename.empty())
    {
        if (!LoadFromFile(filename))
        {
            cout << "Could not load video!" << endl; //Probably should throw exception.
        }
    }
}
 
bool Video::LoadFromFile(const string& filename)
{
    CloseVideo();
    m_sFilename = filename;
    const char * const file = m_sFilename.c_str();
 
    if(av_open_input_file(&m_pFormatCtx, file, NULL, 0, NULL) != 0)
    {
        cout << "Unexisting file!" << endl;;
        return false;
    }
 
    if(av_find_stream_info(m_pFormatCtx) < 0)
    {
        cout << "Couldn't find stream information!" << endl;
        return false;
    }
 
    dump_format(m_pFormatCtx, 0, file, 0);
 
    m_iVideoStream = -1;
    for(nuint i = 0; i < m_pFormatCtx->nb_streams; i++)
    {
        if(m_pFormatCtx->streams[i]->codec->coder_type == CODEC_TYPE_VIDEO)
        {
            m_iVideoStream = i;
            break;
        }
    }
 
    if(m_iVideoStream == -1)
        return false;
 
    m_pCodecCtx = m_pFormatCtx->streams[m_iVideoStream]->codec;
 
    m_pCodec = avcodec_find_decoder(m_pCodecCtx->codec_id);
 
    AVRational r = m_pFormatCtx->streams[m_iVideoStream]->avg_frame_rate;
    AVRational r2 = m_pFormatCtx->streams[m_iVideoStream]->r_frame_rate;
    if ((!r.num || !r.den) &&
        (!r2.num || !r2.den))
    {
        std::cerr << "Video_video::Initialize() - unable to get the video frame rate. Using 25 fps." << std::endl;
        m_fSecondsPerFrame = 1.f / 25;
    }
    else
    {
        if (r.num && r.den)
            m_fSecondsPerFrame = 1.f/((float)r.num / r.den);
        else
            m_fSecondsPerFrame = 1.f/((float)r2.num / r2.den);
    }
 
    if(m_pCodec == NULL)
    {
        cout << "Unsupported codec!" << endl;
        return false;
    }
 
    if(avcodec_open(m_pCodecCtx, m_pCodec) < 0)
    {
        cout << "Could not open Codec Context" << endl;
        return false;
    }
 
    m_iFrameSize = m_pCodecCtx->width * m_pCodecCtx->height * 3;
 
    m_pFrame = avcodec_alloc_frame();
 
    m_pFrameRGB = avcodec_alloc_frame();
 
    if(m_pFrameRGB == NULL || m_pFrame == NULL)
    {
        cout << "Error allocating frame" << endl;
        return false;
    }
 
    int numBytes = avpicture_get_size(PIX_FMT_RGBA, m_pCodecCtx->width, m_pCodecCtx->height);
 
    m_pBuffer = (sf::Uint8 *)av_malloc(numBytes * sizeof(sf::Uint8));
 
    avpicture_fill((AVPicture *)m_pFrameRGB, m_pBuffer, PIX_FMT_RGBA,
                   m_pCodecCtx->width, m_pCodecCtx->height);
 
    img_convert_ctx = sws_getContext(m_pCodecCtx->width, m_pCodecCtx->height,
                                     m_pCodecCtx->pix_fmt,
                                     m_pCodecCtx->width, m_pCodecCtx->height,
                                     PIX_FMT_RGBA, SWS_FAST_BILINEAR,
                                     NULL, NULL, NULL);
    m_bVideoLoaded = true;
    m_bImageNeedsUpdate = true;
 
    m_Image.Create(GetWidth(), GetHeight(), sf::Color::Black);
 
    Update(10000);
 
    return true;
} //Load From File
 
void Video::Update(float time)
{
    if (m_bVideoLoaded)
    {
        m_fTimePassedSinceLastFrameUpdate += time;
        UpdateImage();
 
        if (m_fTimePassedSinceLastFrameUpdate > m_fSecondsPerFrame)
        {
            m_fTimePassedSinceLastFrameUpdate = 0;
            LoadNextFrame();
        }
    }
}
 
void Video::LoadNextFrame()
{
    do
    {
        av_free_packet(&m_Packet);
        int result = av_read_frame(m_pFormatCtx, &m_Packet);
        if (result < 0)
        {
            CloseVideo();
            LoadFromFile(m_sFilename);
            m_fTimePassedSinceLastFrameUpdate = 0;
            return;
        }
    } while (m_Packet.stream_index != m_iVideoStream);
 
    int frameFinished = 0;
    avcodec_decode_video2(m_pCodecCtx, m_pFrame, &frameFinished, &m_Packet);
    if (frameFinished)
    {
        sws_scale(img_convert_ctx, m_pFrame->data, m_pFrame->linesize,
                  0, m_pCodecCtx->height, m_pFrameRGB->data, m_pFrameRGB->linesize);
        m_bImageNeedsUpdate = true;
    }
 
}
 
sf::Color Video::GetPixel(nuint x, nuint y) const
{
    nuint i = 3*(y*GetWidth()+x);
    sf::Uint8 red   = m_pFrameRGB->data[0][i];
    sf::Uint8 green = m_pFrameRGB->data[0][i+1];
    sf::Uint8 blue  = m_pFrameRGB->data[0][i+2];
    return sf::Color(red,green,blue,255);
}
 
void Video::UpdateImage()
{
    if (m_bImageNeedsUpdate)
    {
        m_Image.UpdatePixels(m_pBuffer);
        m_bImageNeedsUpdate = false;
    }
}
 
void Video::CloseVideo()
{
    if (m_bVideoLoaded)
    {
        av_free_packet(&m_Packet);
        av_free(m_pBuffer);
        av_free(m_pFrameRGB);
        av_free(m_pFrame);
        avcodec_close(m_pCodecCtx);
        av_close_input_file(m_pFormatCtx);
        sws_freeContext(img_convert_ctx);
 
        m_bVideoLoaded = false;
        m_bImageNeedsUpdate = false;
    }
}
 
Video::~Video()
{
    CloseVideo();
}

There are still many things to do, such as seeking, pausing, audio, etc. Tell me if you find this useful.

 
en/sources/video_integration.txt · Last modified: 2010/11/02 18:34 by Xorlium
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki