Пример #1
0
void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale) {

	if (p_image.is_null() || p_image->empty())
		return;

	begin_frame(0.0);

	int window_w = OS::get_singleton()->get_video_mode(0).width;
	int window_h = OS::get_singleton()->get_video_mode(0).height;

	glBindFramebuffer(GL_FRAMEBUFFER, RasterizerStorageGLES3::system_fbo);
	glViewport(0, 0, window_w, window_h);
	glDisable(GL_BLEND);
	glDepthMask(GL_FALSE);
	if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) {
		glClearColor(0.0, 0.0, 0.0, 0.0);
	} else {
		glClearColor(p_color.r, p_color.g, p_color.b, 1.0);
	}
	glClear(GL_COLOR_BUFFER_BIT);
	canvas->canvas_begin();

	RID texture = storage->texture_create();
	storage->texture_allocate(texture, p_image->get_width(), p_image->get_height(), 0, p_image->get_format(), VS::TEXTURE_TYPE_2D, VS::TEXTURE_FLAG_FILTER);
	storage->texture_set_data(texture, p_image);

	Rect2 imgrect(0, 0, p_image->get_width(), p_image->get_height());
	Rect2 screenrect;
	if (p_scale) {

		if (window_w > window_h) {
			//scale horizontally
			screenrect.size.y = window_h;
			screenrect.size.x = imgrect.size.x * window_h / imgrect.size.y;
			screenrect.position.x = (window_w - screenrect.size.x) / 2;

		} else {
			//scale vertically
			screenrect.size.x = window_w;
			screenrect.size.y = imgrect.size.y * window_w / imgrect.size.x;
			screenrect.position.y = (window_h - screenrect.size.y) / 2;
		}
	} else {

		screenrect = imgrect;
		screenrect.position += ((Size2(window_w, window_h) - screenrect.size) / 2.0).floor();
	}

	RasterizerStorageGLES3::Texture *t = storage->texture_owner.get(texture);
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, t->tex_id);
	canvas->draw_generic_textured_rect(screenrect, Rect2(0, 0, 1, 1));
	glBindTexture(GL_TEXTURE_2D, 0);
	canvas->canvas_end();

	storage->free(texture); // free since it's only one frame that stays there

	end_frame(true);
}
Пример #2
0
void sound_add_cycles(unsigned c) {
#if !defined(PSP) && !defined(EMSCRIPTEN)
    cycles += c;
    if (cycles >= MAX_CYCLES) {
        cycles -= MAX_CYCLES;
        end_frame();
    }
#endif
}
Пример #3
0
blargg_err_t SNES_SPC::play( int count, sample_t* out )
{
	require( (count & 1) == 0 ); // must be even
	if ( count )
	{
		set_output( out, count );
		end_frame( count * (clocks_per_sample / 2) );
	}
	
	const char* err = m.cpu_error;
	m.cpu_error = 0;
	return err;
}
Пример #4
0
void Renderer::render_frame()
{
	begin_frame();
	
	if (scene_to_render != nullptr)
	{
		pre_render();

		gbuffer_render();

		main_render();

		post_render();
	}

	gui.render_frame();

	end_frame();
}
Пример #5
0
int main(int argc, char *argv[])
{
    const fixed dt = (1 << FIXPOINT_SHIFT) / 100;   // 60 FPS
    fixed t = 0;
    fixed accumulator = 0;
    fixed delta_time = 0;
    SDL_Event event;

    init();

    while (!app.quit)
    {
        start_frame();

        delta_time = get_last_frame_time();

        if (SDL_PollEvent(&event))
        {
            handle_event(&event);
        }

        if (!app.paused)
        {
            accumulator += delta_time;
//            trace("accumulator %ld, dt: %ld, delta_time %ld", accumulator, dt, delta_time);

            while (accumulator >= dt)
            {
                move_objects(t, dt);
                t += dt;
                accumulator -= dt;
            }

            interpolate(fpdiv(accumulator, dt));

            draw_scene();
        }

        end_frame();
    }

    return 0;
}
Пример #6
0
/* switch client to new state */
void set_state( int newstate )
{
	if ( client_state == newstate ) return;
	
	if ( newstate == CS_PLAY )
		grab_input( 1 );
	if ( client_state == CS_PLAY )
		grab_input( 0 );
	if ( client_state == CS_CONFIRM_WARP ||
	     client_state == CS_CONFIRM_RESTART ||
	     client_state == CS_CONFIRM_QUIT ||
             client_state == CS_CONFIRM_CONTINUE ||
	     client_state == CS_GET_READY ||
	     client_state == CS_PAUSE ||
	     client_state == CS_FINAL_PLAYER_INFO ||
         client_state == CS_GET_READY_FOR_NEXT_LEVEL ||
	     client_state == CS_RECV_LEVEL ||
	     client_state == CS_ROUND_RESULT ||
	     client_state == CS_RECV_STATS ||
	     client_state == CS_FATAL_ERROR ) {
		/* show offscreen */
		if ( offscreen ) {
			stk_surface_blit( offscreen, 0,0,-1,-1, stk_display, 0,0 );
			end_frame();
		}
		/* do not refresh when coming from RECV_LEVEL as a GET_READY
		 * will follow */
		if ( client_state != CS_RECV_LEVEL )
		if ( client_state != CS_ROUND_RESULT )
		if ( client_state != CS_RECV_STATS );
			stk_display_update( STK_UPDATE_ALL );
	}

	client_state = newstate;
	stk_timer_reset();
}
Пример #7
0
void Profiler::begin_frame()
{
    // End the previous frame if any
    end_frame();
    begin_block("RunFrame");
}
Пример #8
0
static void filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref)
{
    end_frame(link);
}
Пример #9
0
static int filter_samples(AVFilterLink *link, AVFilterBufferRef *samplesref)
{
    end_frame(link);
    return 0;
}
Пример #10
0
/* run the state driven loop until game is broken up or finished */
void client_game_run( void )
{
	int ms, frame_delay = config.fps?10:1;
	int button_clicked, key_pressed;
	SDL_Event event;
	int abort = 0, i, j, penalty;
	/* frame rate */
	int frames = 0;
	int frame_time = SDL_GetTicks();

	event_clear_sdl_queue();
	
	stk_display_fade( STK_FADE_IN, STK_FADE_DEFAULT_TIME );
	
	stats_received = 0;
	stk_timer_reset(); ms = 1;
	while ( !abort && !stk_quit_request ) {
		/* check wether an event occured */
		button_clicked = key_pressed = 0;
		if ( SDL_PollEvent( &event ) ) {
			if ( client_state == CS_PAUSE && game->game_type == GT_NETWORK )
				gui_dispatch_event( &event, ms );
			else
			if ( event.type == SDL_MOUSEBUTTONDOWN )
				button_clicked = event.button.button;
			else
			if ( event.type == SDL_KEYDOWN ) {
				key_pressed = event.key.keysym.sym;
				if ( handle_default_key( key_pressed, &abort ) )
					key_pressed = 0;
			}
			else
			if (event.type == SDL_ACTIVEEVENT)
		          {
			    if (event.active.state == SDL_APPINPUTFOCUS ||
				event.active.state == SDL_APPACTIVE )
                            if (event.active.gain == 0 )
			      client_set_pause(1);
			  }
		}
		else if ( client_state == CS_PAUSE && game->game_type == GT_NETWORK )
			gui_dispatch_event( 0, ms );

		/* check whether Shift is pressed to switch between own and highest score */
		if (game->game_type == GT_LOCAL)
		  handle_display_switch();

		/* let server know we're still alive except
		 * in CS_PLAY as we send paddle updates there */
		if ( game->game_type == GT_NETWORK )
			comm_send_heartbeat();

		/* handle client */
		switch ( client_state ) {

		case CS_FINAL_STATS:
			if ( key_pressed==SDLK_SPACE ) abort = 1;
			break;
			
		case CS_FATAL_ERROR:
			/* after game was violently broken up the server
			 * may still send the stats of the game so far */
			if ( button_clicked || key_pressed ) {
				SDL_Delay(250); /* give time to release button */
				set_state( CS_RECV_STATS );
				display_text( font, "Receiving final stats..." );
			}
			break;
			
		case CS_FINAL_TABLE:
			if ( button_clicked || key_pressed ) {
				check_highscores();
				select_chart( game_set->name, 0 );
                                /* remove saved game */
                                slot_delete( 0 );
                                slot_update_hint( 0, item_resume_0->hint );
				/* quit local game */
				abort = 1;
			}
			break;

		case CS_SCORE_TABLE:
			/* show who's next player and scores in local game */
			display_score_table( "Next Player: %s", cur_player->name );
			set_state( CS_GET_READY );
			break;
        
        case CS_BONUS_LEVEL_SCORE:
            /* display total score from this level for player */
            display_bonus_level_score();
			set_state( CS_GET_READY_FOR_NEXT_LEVEL );
            break;
			
		case CS_FINAL_PLAYER_INFO:
			if ( button_clicked || key_pressed ) {
				SDL_Delay(250); /* give time to release button */
				set_state( CS_NEXT_PLAYER );
			}
			break;

		case CS_RECV_LEVEL:
			comm_recv();
			if ( cur_player->next_level_received ) {
				cur_player->next_level_received = 0;
				cur_player->paddle_id = cur_player->next_paddle_id;
				init_next_round();
			}
			break;

		case CS_RECV_STATS:
			comm_recv();
			if ( stats_received ) {
				set_state( CS_FINAL_STATS );
				display_final_stats();
			}
			break;
			
		case CS_ROUND_RESULT:
			if ( button_clicked || key_pressed ) {
				SDL_Delay(250); /* give time to release button */
				if ( game_over ) {
					set_state( CS_RECV_STATS );
					display_text( font, "Receiving final stats..." );
				} else {
					set_state( CS_RECV_LEVEL );
					display_text( font, "Receiving level data..." );
 				}
			}
			break;
			
		case CS_GET_READY:
			if ( button_clicked || key_pressed ) {
				SDL_Delay(250); /* give time to release button */
				comm_send_short( MSG_READY );
				set_state( CS_PLAY );
			}
			break;

		case CS_GET_READY_FOR_NEXT_LEVEL:
			if ( button_clicked || key_pressed ) {
				SDL_Delay(250); /* give time to release button */
				set_state( CS_NEXT_LEVEL );
			}
			break;

		case CS_PAUSE:
			if ( game->game_type == GT_LOCAL ) break;

			/* check wether pause chatroom has been closed
			 * either by client or remote */
			comm_recv();
			break;
			
		case CS_PLAY:
			/* hide objects */
			begin_frame();
			
			/* apply events to local paddle */
			paddle_handle_events( l_paddle, ms );

			/* update local objects and communicate if
			 * comm_delay ms have passed */
			update_game( ms );
			
			/* show objects */
			end_frame();

			/* handle local level over */
			if ( game->level_over ) {
				if ( game->game_type == GT_LOCAL ) {
					if ( game_set == 0 ) {
						abort = 1; /* was a test level */
						grab_input(0);
						break;
					}
					if ( game->winner == PADDLE_BOTTOM )
                    {
                        if (local_game->isBonusLevel)
                            set_state( CS_BONUS_LEVEL_SCORE );
                        else
                            set_state( CS_NEXT_LEVEL );
                    }
					else
						set_state( CS_LOOSE_LIFE );
				} else {
					finalize_round();
				}
			}
			break;

		case CS_NEXT_LEVEL:
			/* apply paddle stats to player */
			game_set_current( local_game );
			game_update_stats( PADDLE_BOTTOM, &cur_player->stats );
			game_set_current( game );
			/* init next level for player in local game */
			cur_player->level_id++;
			if ( cur_player->level_id >= game_set->count ) {
				/* deactivate player */
				cur_player->lives = 0;
				display_text( font, 
					"You've cleared all levels...#Congratulations!!!" );
				set_state( CS_FINAL_PLAYER_INFO );
				break;
			}
			/* get snapshot for next init */
			cur_player->snapshot = *game_set->levels[cur_player->level_id];
			/* cycle players */
			set_state( CS_NEXT_PLAYER );
			break;

		case CS_RESTART_LEVEL:
			/* apply paddle stats to player */
			game_set_current( local_game );
			game_update_stats( PADDLE_BOTTOM, &cur_player->stats );
			game_set_current( game );
			/* reset level for next turn */
			cur_player->snapshot = *game_set->levels[cur_player->level_id];
			/* decrease lives (is checked that this wasn't the last one) */
			cur_player->lives--;
			/* cycle players */
			set_state( CS_NEXT_PLAYER );
			break;
			
		case CS_LOOSE_LIFE:
			/* apply paddle stats to player */
			game_set_current( local_game );
			game_update_stats( PADDLE_BOTTOM, &cur_player->stats );
			game_set_current( game );

			/* remember level for next turn */
			game_get_level_snapshot( &cur_player->snapshot );

			/* decrease lives */
			cur_player->lives--;
			if ( cur_player->lives == 0 ) {
				display_text( font, 
					"You've lost all lives...#Do you want to buy a continue#for 100%% of your score? y/n" );
                                set_state( CS_CONFIRM_CONTINUE );
				//set_state( CS_FINAL_PLAYER_INFO );
				break;
			}
			set_state( CS_NEXT_PLAYER );
			break;

		case CS_NEXT_PLAYER:
			/* game over? */
			if ( players_count() == 0 ) {
				display_score_table( "Game Over!" );
				set_state( CS_FINAL_TABLE );
				break;
			}
			/* speak and fade */
			play_speech();
			fade_anims();
			/* finalize current game context */
			finalize_level();
			/* set next player */
			cur_player = players_get_next();
			init_level( cur_player, PADDLE_BOTTOM );
			if ( player_count > 1 )
				set_state( CS_SCORE_TABLE );
			else {
				set_state( CS_PLAY ); /* one player starts immediately */
				stk_display_update( STK_UPDATE_ALL );
			}
			break;
		
                case CS_CONFIRM_CONTINUE:
		case CS_CONFIRM_QUIT:
		case CS_CONFIRM_WARP:
		case CS_CONFIRM_RESTART:
			if ( key_pressed == 0 ) break;
			if ( key_pressed==SDLK_n||key_pressed==SDLK_ESCAPE ) {
                            /* if denying continue... DIE!!! */
                            if ( client_state == CS_CONFIRM_CONTINUE )
                            {
				SDL_Delay(250); /* give time to release button */
				set_state( CS_NEXT_PLAYER );
                                //set_state( CS_FINAL_PLAYER_INFO );
                            }
                            else
				set_state( CS_PLAY );
			    break;
			}
			if ( key_pressed != SDLK_y && key_pressed != SDLK_z ) break;
			/* handle confirmed action */
			SDL_Delay(250); /* give time to release button */
			switch( client_state ) {
                                case CS_CONFIRM_CONTINUE:
                                    /* clear score and give full lives again */
                                    cur_player->lives = game->diff->lives;
                                    cur_player->stats.total_score = 0;
                                    set_state( CS_NEXT_PLAYER );
                                    break;
				case CS_CONFIRM_QUIT:
					comm_send_short( MSG_QUIT_GAME );
					if ( game->game_type == GT_LOCAL ) {
						/* apply paddle stats to player */
						game_set_current( local_game );
						game_update_stats( PADDLE_BOTTOM, &cur_player->stats );
						game_set_current( game );
                                                /* no higscore check anymore as game is supposed to
                                                 * be resumed until normal game over */
						/* testing levels don't got for
						 * high scores ***
						if ( game_set ) {
							check_highscores();
							select_chart( game_set->name, 0 );
						}*/
                                                /* save local game */
                                                if ( game_set != 0 /*not testing a level*/ )
                                                    save_local_game( 0 );
						
                                                abort = 1;
					}
					else {
						/* await game stats */
						set_state( CS_RECV_STATS );
						display_text( font, "Receiving final stats..." );
					}
					break;
				case CS_CONFIRM_WARP:
					game->winner = -1; /* no speech */
					local_game->winner = -1; /* not counted as win */
                                        /* substract doubled score of remaining bricks */
                                        penalty = 0;
                                        for ( i = 0; i < MAP_WIDTH; i++ )
                                            for ( j = 0; j < MAP_HEIGHT; j++ )
                                                if ( local_game->bricks[i][j].dur != -1 )
                                                    penalty += local_game->bricks[i][j].score;
                                        printf( "warp penalty: -%d\n", penalty );
                                        local_game->paddles[0]->score -= penalty;
					set_state( CS_NEXT_LEVEL );
					break;
				case CS_CONFIRM_RESTART:
					game->winner = -1; /* no speech */
					local_game->winner = -1; /* not counted as win */
					local_game->level_over = 1;
					set_state( CS_RESTART_LEVEL );
					break;
			}
			break;

		}

		/* update anything that was changed */
		stk_display_update( STK_UPDATE_RECTS );

		/* get time since last call and delay if below frame_delay */
		ms = stk_timer_get_time();
		if ( ms < frame_delay ) {
			SDL_Delay( frame_delay - ms );
			ms += stk_timer_get_time();
		}
		frames++;
	}
	finalize_level();
	client_state = CLIENT_NONE;

	stk_display_fade( STK_FADE_OUT, STK_FADE_DEFAULT_TIME );
	if ( stk_quit_request )
		comm_send_short( MSG_DISCONNECT );
	else
		comm_send_short( MSG_UNHIDE );

	/* frame rate */
	frame_time = SDL_GetTicks() - frame_time;
	printf( "Time: %.2f, Frames: %i -> FPS: %.2f\n", 
		(double)frame_time / 1000, frames, 1000.0*frames/frame_time );

	event_clear_sdl_queue();

	/* update the selected user and the user list in network as 
	 * we received ADD/REMOVE_USER messages */
	gui_list_update( list_users, client_users->count );
	/* re-select current entry */
	if ( client_user ) {
		i = list_check( client_users, client_user );
		if ( i != -1 )
			gui_list_select( list_users, 0, i, 1 );
	}
}
Пример #11
0
void VideoViewer(const std::string& input_uri, const std::string& output_uri)
{
    pangolin::Var<int>  record_timelapse_frame_skip("viewer.record_timelapse_frame_skip", 1 );
    pangolin::Var<int>  end_frame("viewer.end_frame", std::numeric_limits<int>::max() );
    pangolin::Var<bool> video_wait("video.wait", true);
    pangolin::Var<bool> video_newest("video.newest", false);

    // Open Video by URI
    pangolin::VideoRecordRepeat video(input_uri, output_uri);
    const size_t num_streams = video.Streams().size();

    if(num_streams == 0) {
        pango_print_error("No video streams from device.\n");
        return;
    }

    // Output details of video stream
    for(size_t s = 0; s < num_streams; ++s) {
        const pangolin::StreamInfo& si = video.Streams()[s];
        std::cout << "Stream " << s << ": " << si.Width() << " x " << si.Height()
                  << " " << si.PixFormat().format << " (pitch: " << si.Pitch() << " bytes)" << std::endl;
    }

    // Check if video supports VideoPlaybackInterface
    pangolin::VideoPlaybackInterface* video_playback = pangolin::FindFirstMatchingVideoInterface<pangolin::VideoPlaybackInterface>(video);
    const int total_frames = video_playback ? video_playback->GetTotalFrames() : std::numeric_limits<int>::max();
    const int slider_size = (total_frames < std::numeric_limits<int>::max() ? 20 : 0);

    if( video_playback ) {
        if(total_frames < std::numeric_limits<int>::max() ) {
            std::cout << "Video length: " << total_frames << " frames" << std::endl;
        }
        end_frame = 0;
    }

    std::vector<unsigned char> buffer;
    buffer.resize(video.SizeBytes()+1);

    // Create OpenGL window - guess sensible dimensions
    pangolin::CreateWindowAndBind( "VideoViewer",
        (int)(video.Width() * num_streams),
        (int)(video.Height() + slider_size)
    );

    // Assume packed OpenGL data unless otherwise specified
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glPixelStorei(GL_PACK_ALIGNMENT, 1);
    glEnable (GL_BLEND);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // Setup resizable views for video streams
    std::vector<pangolin::GlPixFormat> glfmt;
    std::vector<std::pair<float,float> > gloffsetscale;
    std::vector<size_t> strides;
    std::vector<pangolin::ImageViewHandler> handlers;
    handlers.reserve(num_streams);

    size_t scratch_buffer_bytes = 0;

    pangolin::View& container = pangolin::Display("streams");
    container.SetLayout(pangolin::LayoutEqual)
             .SetBounds(pangolin::Attach::Pix(slider_size), 1.0, 0.0, 1.0);
    for(unsigned int d=0; d < num_streams; ++d) {
        const pangolin::StreamInfo& si = video.Streams()[d];
        pangolin::View& view = pangolin::CreateDisplay().SetAspect(si.Aspect());
        container.AddDisplay(view);
        glfmt.push_back(pangolin::GlPixFormat(si.PixFormat()));
        gloffsetscale.push_back(std::pair<float,float>(0.0f, 1.0f) );
        if( si.PixFormat().bpp % 8 ) {
            pango_print_warn("Stream %i: Unable to display formats that are not a multiple of 8 bits.", d);
        }
        if( (8*si.Pitch()) % si.PixFormat().bpp ) {
            pango_print_warn("Stream %i: Unable to display formats whose pitch is not a whole number of pixels.", d);
        }
        if(glfmt.back().gltype == GL_DOUBLE) {
            scratch_buffer_bytes = std::max(scratch_buffer_bytes, sizeof(float)*si.Width() * si.Height());
        }
        strides.push_back( (8*si.Pitch()) / si.PixFormat().bpp );
        handlers.push_back( pangolin::ImageViewHandler(si.Width(), si.Height()) );
        view.SetHandler(&handlers.back());
    }

    // current frame in memory buffer and displaying.
    pangolin::Var<int> frame("ui.frame", -1, 0, total_frames-1 );
    pangolin::Slider frame_slider("frame", frame.Ref() );
    if(video_playback && total_frames < std::numeric_limits<int>::max())
    {
        frame_slider.SetBounds(0.0, pangolin::Attach::Pix(slider_size), 0.0, 1.0);
        pangolin::DisplayBase().AddDisplay(frame_slider);
    }

    std::vector<unsigned char> scratch_buffer;
    scratch_buffer.resize(scratch_buffer_bytes);

    std::vector<pangolin::Image<unsigned char> > images;

#ifdef CALLEE_HAS_CPP11
    const int FRAME_SKIP = 30;
    const char show_hide_keys[]  = {'1','2','3','4','5','6','7','8','9'};
    const char screenshot_keys[] = {'!','"','#','$','%','^','&','*','('};

    // Show/hide streams
    for(size_t v=0; v < container.NumChildren() && v < 9; v++) {
        pangolin::RegisterKeyPressCallback(show_hide_keys[v], [v,&container](){
            container[v].ToggleShow();
        } );
        pangolin::RegisterKeyPressCallback(screenshot_keys[v], [v,&images,&video](){
            if(v < images.size() && images[v].ptr) {
                try{
                    pangolin::SaveImage(
                        images[v], video.Streams()[v].PixFormat(),
                        pangolin::MakeUniqueFilename("capture.png")
                    );
                }catch(std::exception e){
                    pango_print_error("Unable to save frame: %s\n", e.what());
                }
            }
        } );
    }

    pangolin::RegisterKeyPressCallback('r', [&](){
        if(!video.IsRecording()) {
            video.SetTimelapse( static_cast<size_t>(record_timelapse_frame_skip) );
            video.Record();
            pango_print_info("Started Recording.\n");
        }else{
            video.Stop();
            pango_print_info("Finished recording.\n");
        }
        fflush(stdout);
    });
    pangolin::RegisterKeyPressCallback('p', [&](){
        video.Play();
        end_frame = std::numeric_limits<int>::max();
        pango_print_info("Playing from file log.\n");
        fflush(stdout);
    });
    pangolin::RegisterKeyPressCallback('s', [&](){
        video.Source();
        end_frame = std::numeric_limits<int>::max();
        pango_print_info("Playing from source input.\n");
        fflush(stdout);
    });
    pangolin::RegisterKeyPressCallback(' ', [&](){
        end_frame = (frame < end_frame) ? frame : std::numeric_limits<int>::max();
    });
    pangolin::RegisterKeyPressCallback('w', [&](){
        video_wait = !video_wait;
        if(video_wait) {
            pango_print_info("Gui wait's for video frame.\n");
        }else{
            pango_print_info("Gui doesn't wait for video frame.\n");
        }
    });
    pangolin::RegisterKeyPressCallback('d', [&](){
        video_newest = !video_newest;
        if(video_newest) {
            pango_print_info("Discarding old frames.\n");
        }else{
            pango_print_info("Not discarding old frames.\n");
        }
    });
    pangolin::RegisterKeyPressCallback('<', [&](){
        if(video_playback) {
            frame = video_playback->Seek(frame - FRAME_SKIP) -1;
            end_frame = frame + 1;
        }else{
            pango_print_warn("Unable to skip backward.");
        }
    });
    pangolin::RegisterKeyPressCallback('>', [&](){
        if(video_playback) {
            frame = video_playback->Seek(frame + FRAME_SKIP) -1;
            end_frame = frame + 1;
        }else{
            end_frame = frame + FRAME_SKIP;
        }
    });
    pangolin::RegisterKeyPressCallback(',', [&](){
        if(video_playback) {
            frame = video_playback->Seek(frame - 1) -1;
            end_frame = frame+1;
        }else{
            pango_print_warn("Unable to skip backward.");
        }
    });
    pangolin::RegisterKeyPressCallback('.', [&](){
        // Pause at next frame
        end_frame = frame+1;
    });
    pangolin::RegisterKeyPressCallback('0', [&](){
        video.RecordOneFrame();
    });
    pangolin::RegisterKeyPressCallback('a', [&](){
        // Adapt scale
        for(unsigned int i=0; i<images.size(); ++i) {
            if(container[i].HasFocus()) {
                pangolin::Image<unsigned char>& img = images[i];
                pangolin::ImageViewHandler& ivh = handlers[i];

                const bool have_selection = std::isfinite(ivh.GetSelection().Area()) && std::abs(ivh.GetSelection().Area()) >= 4;
                pangolin::XYRangef froi = have_selection ? ivh.GetSelection() : ivh.GetViewToRender();
                gloffsetscale[i] = pangolin::GetOffsetScale(img, froi.Cast<int>(), glfmt[i]);
            }
        }
    });
    pangolin::RegisterKeyPressCallback('g', [&](){
        std::pair<float,float> os_default(0.0f, 1.0f);

        // Get the scale and offset from the container that has focus.
        for(unsigned int i=0; i<images.size(); ++i) {
            if(container[i].HasFocus()) {
                pangolin::Image<unsigned char>& img = images[i];
                pangolin::ImageViewHandler& ivh = handlers[i];

                const bool have_selection = std::isfinite(ivh.GetSelection().Area()) && std::abs(ivh.GetSelection().Area()) >= 4;
                pangolin::XYRangef froi = have_selection ? ivh.GetSelection() : ivh.GetViewToRender();
                os_default = pangolin::GetOffsetScale(img, froi.Cast<int>(), glfmt[i]);
                break;
            }
        }

        // Adapt scale for all images equally
        // TODO : we're assuming the type of all the containers images' are the same.
        for(unsigned int i=0; i<images.size(); ++i) {
            gloffsetscale[i] = os_default;
        }

    });
#endif // CALLEE_HAS_CPP11

#ifdef DEBUGVIDEOVIEWER
    unsigned int delayms = 0;
    pangolin::RegisterKeyPressCallback('z', [&](){
      // Adapt delay
      delayms += 1;
      std::cout << "                  Fake delay " << delayms << "ms" << std::endl;
    });

    pangolin::RegisterKeyPressCallback('x', [&](){
      // Adapt delay
      delayms = (delayms > 1) ? delayms-1 : 0;
    });

    pangolin::basetime start,now;
#endif // DEBUGVIDEOVIEWER

    // Stream and display video
    while(!pangolin::ShouldQuit())
    {
        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
        glColor3f(1.0f, 1.0f, 1.0f);

        if(frame.GuiChanged()) {
            if(video_playback) {
                frame = video_playback->Seek(frame) -1;
            }
            end_frame = frame + 1;
        }

#ifdef DEBUGVIDEOVIEWER
        boostd::this_thread::sleep_for(boostd::chrono::milliseconds(delayms));
        std::cout << "-------------------------------------------------------" << std::endl;
        now = pangolin::TimeNow();
        std::cout << "      FPS: " << 1.0/pangolin::TimeDiff_s(start, now) << " artificial delay: " << delayms <<"ms"<< std::endl;
        std::cout << "-------------------------------------------------------" << std::endl;
        start = now;
#endif
        if ( frame < end_frame ) {
            if( video.Grab(&buffer[0], images, video_wait, video_newest) ) {
                frame = frame +1;
            }
        }
#ifdef DEBUGVIDEOVIEWER
        const pangolin::basetime end = pangolin::TimeNow();
        std::cout << "Total grab time: " << 1000*pangolin::TimeDiff_s(start, end) << "ms" << std::endl;
#endif

        glLineWidth(1.5f);
        glDisable(GL_DEPTH_TEST);

        for(unsigned int i=0; i<images.size(); ++i)
        {
            if(container[i].IsShown()) {
                container[i].Activate();
                pangolin::Image<unsigned char>& image = images[i];

                // Get texture of correct dimension / format
                const pangolin::GlPixFormat& fmt = glfmt[i];
                pangolin::GlTexture& tex = pangolin::TextureCache::I().GlTex((GLsizei)image.w, (GLsizei)image.h, fmt.scalable_internal_format, fmt.glformat, GL_FLOAT);

                // Upload image data to texture
                tex.Bind();
                if(fmt.gltype == GL_DOUBLE) {
                    // Convert to float first, using scrath_buffer for storage
                    pangolin::Image<float> fimage(image.w, image.h, image.w*sizeof(float), (float*)scratch_buffer.data());
                    ConvertPixels<float,double>( fimage, image.Reinterpret<double>() );
                    glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
                    tex.Upload(fimage.ptr,0,0, (GLsizei)fimage.w, (GLsizei)fimage.h, fmt.glformat, GL_FLOAT);
                }else{
                    glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)strides[i]);
                    tex.Upload(image.ptr,0,0, (GLsizei)image.w, (GLsizei)image.h, fmt.glformat, fmt.gltype);
                }

                // Render
                handlers[i].UpdateView();
                handlers[i].glSetViewOrtho();
                const std::pair<float,float> os = gloffsetscale[i];
                pangolin::GlSlUtilities::OffsetAndScale(os.first, os.second);
                handlers[i].glRenderTexture(tex);
                pangolin::GlSlUtilities::UseNone();
                handlers[i].glRenderOverlay();
            }
        }
        glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);

        // leave in pixel orthographic for slider to render.
        pangolin::DisplayBase().ActivatePixelOrthographic();
        if(video.IsRecording()) {
            pangolin::glRecordGraphic(pangolin::DisplayBase().v.w-14.0f, pangolin::DisplayBase().v.h-14.0f, 7.0f);
        }
        pangolin::FinishFrame();
    }
}