4k video playback for VR

Hi all,

I´m about to start a VR video player project with some 4K res file. I know that the current video solution for windows is not the best one (quicktime).

I would like some suggestions on what path to follow, like cinder blocks, video codecs etc…

thanks!
cool new forum btw : )

mayve you can use opencv ? i have very recently switched from quicktime to opencv (but i have not tested performance with 4k videos).

very simple, quick and dirty sample code below (some functions are not even complete) :slight_smile:

#pragma once
#include <opencv2/opencv.hpp>
#include <exception>
#include <chrono>

#ifdef _DEBUG
#pragma comment(lib, "opencv_world310d.lib")
#else
#pragma comment(lib, "opencv_world310.lib")
#endif

class Cv_movie
{
protected:
    
    // buffers
    IplImage* frame = nullptr;
    unsigned int frame_counter = 0;
    CvCapture* capture = nullptr;

    // timing stuff
    double fps = 0;
    double dt = 0;
    chrono::high_resolution_clock::time_point time_last_update;
    double time_movie = 0; // timestamp of the movie
    double time_simul = 0; // timestamp of the simulation

    // movie properties
    bool is_playing_ = false;
    int w = 0;
    int h = 0;
    int n_frames = 0;
    string file_Name;
public:
    Cv_movie()
    {

    }

    ~Cv_movie()
    {
        cvReleaseCapture(&capture);
    }
    double get_time() { return time_movie; }
    unsigned int get_frame() { return frame_counter; }
    void seek_to_start()
    {
        // seek to first frame
        cvSetCaptureProperty(capture, CV_CAP_PROP_POS_FRAMES, 0);
        frame_counter = 0;
        time_movie = 0;
        time_simul = 0;
    }
    void play()
    {
        is_playing_ = true;
    }
    void stop()
    {
        is_playing_ = false;
    }
    bool is_playing() { return is_playing_; }
    void set_volume(float v) {}
    void set_loop(bool b) {}
    bool get_loop() { return false;  } // is the movie looping ?
    void setup(const char* fname) { setup(fname); }
    void setup(const string& fname)
    {
        capture = cvCreateFileCapture(fname.c_str());
        
        if (!capture)
        {
            auto msg = "could not open Video file using opencv";
            cerr <<  msg << " file name: " << fname << endl;
            throw(exception(msg));
        }

        w = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
        h = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);
        fps = cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);
        n_frames = (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_COUNT);
        file_Name = fname;
        
        if (fps > 0) { dt = 1.0 / fps; }

        printf("Video Size = %d x %d\n", w, h);
        printf("FPS = %f\number of frames = %d\n", fps, n_frames);
    }

    // returns true if a new frame was grabbed
    // if force_grab == true, a single new frame is grabbed independently of the movie's framerate and the movie time is increased by 1.0/fps
    bool update(bool force_grab_single_frame = false)
    {    
        int n_frames_to_grab = 0;

        using namespace chrono;
        auto t_now = high_resolution_clock::now();

        if (is_playing_)
        {
            // movie startup: grab the very first frame independent of timers
            if (frame_counter == 0) 
            {
                force_grab_single_frame = true;
                time_last_update = chrono::high_resolution_clock::now();
            }

            duration<double> dt_update = duration_cast<duration<double>>(t_now - time_last_update);

            time_simul += dt_update.count();

            n_frames_to_grab = floor((time_simul - time_movie) / dt);
            
            if (force_grab_single_frame) { n_frames_to_grab = 1; }

            for (int i = 0; i < n_frames_to_grab; i++)
            {
                frame = cvQueryFrame(capture);
                if (frame)
                {
                    frame_counter++;
                    time_movie += dt;
                }
                else
                {
                    //printf("Capture Finished\n");
                    is_playing_ = false;
                    break;
                }
            }
        }

        time_last_update = t_now;

        return (n_frames_to_grab > 0);
    }
};

class Cinder_movie : public Cv_movie
{
protected:
    Surface8u surf;
    Texture2dRef texture;
public:

    Cinder_movie()
    {
    }
    void setup(const string& fname) 
    {
        Cv_movie::setup(fname);
    }

    void update()
    {
        // if a new frame was grabbed, set the cinder surface and upload a texture to the graphics card
        if (Cv_movie::update())
        {
            /*
            SurfaceT (T *data, int32_t width, int32_t height, int32_t rowBytes, SurfaceChannelOrder channelOrder)
            Constructs a surface from the memory pointed to by data . Does not assume ownership of the memory in data , which consequently should not be freed while the Surface is still in use.
            */

            surf = Surface8u((uint8_t*)frame->imageData, frame->width, frame->height, frame->widthStep, SurfaceChannelOrder::BGR);
            texture = Texture2d::create(surf);
            
        }
    }
    cinder::gl::TextureRef get_texture()
    {
        return texture;
    }
};

Hello,
Very interesting !
Are you happy with OpenCV player ? Can you share experience with this ?
I want to try your code !
Which OpenCV block do you use please ?

Thanks

Just this past weekend did tons of 4k video playback on Windows with the hap codec and Éric’s block here: https://github.com/mpcdigital/Cinder-Hap2 Encoding isn’t very fast, but the plackback was fast and reliable. I recommend it!

Hi @colin.

I ended up using this block:


Performance was pretty good, even on a really crappy computer.

@sharkbox, thanks for pointing it out, did you use any specific hardware, or just a plain macbook?

Used a pretty decent PC tower. Nothing mindblowing, but it did have a GTX 1080 which is important for hap, as it’s all decoded on the GPU at the expense of file size. Expect your files to be several GB, but if you have a decent SSD, it’s not a problem.

I used cinder-Hap2 for other project ! It’s very nice !
Hap is very performant but when I seek video, sometimes it lags !
Perhaps, It’s better to bufferise the video into RAM ?

Hey @sharkbox,

Thanks for sharing. I just wanted to confirm, you managed to get Eric’s block (Cinder-Hap2) working in windows in Cinder 0.9.0?

Thanks!

Yup, that’s correct. It might have been a slightly more recent pull request than 0.9.0, but it should work!

Hello,

Perhaps HAP for directshow is a good solution for those who are fake with quicktime.

http://renderheads.com/product/hap-for-directshow/

https://github.com/secondstory/ofxDSHapVideoPlayer
I think it’s not hardly to port this addon to libcinder !

If you are tested this, can you share your experience ?

Thanks

Hello,
Cinder-WMFVideo is very performant player, it can read a lot of movie format.
I read MP4 h264 2160p 60ips with zero lag !

Sometimes the player freeze with video test in samples\SimplePlayback\assets

How to improve stability ?

Which is the best format for stability and performance to use in random read or sequential read ?

Thanks

How I wish Cinder-WMFVideo could record video to file.