Simple Video Tutorial

I’m working with some raw video and audio grabbers and needed a mechanism to view and hear it.
Web searching (of course) led me to
http://www.webkinesia.com/games/rawvideo.php

Eagerly I grabbed the code, fired up the rusty VS 2008 studio, downloaded the SDL (http://www.libsdl.org/download-1.2.php)
Built the code and NO windows opens, exits from the fopen() call. Opps back to the link to grab the sample video and rename to drop the .zip extension.
Well, that allows things to proceed BUT imagine my surprise when, NO screen output.

I had run into a learning curve with SDL. So, I modified the code to use a bltable screen. I also wished to share some of the lessons learned and provide some examples. I see several elements of confusion about some of the SDL 1.2 APIs.

At first, I was concerned about VS 2008 or being on Windows 7. So I grabbed the source and built it. Nice. Provided a .sln from VS 2005.
Build no errors, no warning.

Copied the debug build libs and dll into place and rebuild. Traced into the calls and was met with a compiler gotcha.
The this->hidden->screen_bmp was from a directx declaration (dx5video.h) NOT for the DIB stuff.
What(?).

Well, I went back to the SDL lib project. Added instrumentation, rebuit and looked at the class view. Everything looked fine. Aha. Client build versus lib build headers.
So, the lib appears to be ok.
Next I ruled out the possibility of a corrupt input file by downloading a different viewer. It played (with a little prompting) just great.

So, it’s NOT the lib, it’s NOT the file, What is wrong?

So I looked into the SDL docs to see if there was another way to do the same thing. Yep, use SDL_CreateRGBSurfaceFrom()!
So I modified the code to check for a cmd line arg and use a blt from our buffer

if ( argc > 1 && (strcmp(argv[1], “-blt”) == 0) ) {

//create our own in memory 'screen buffer - a DIB_Bitmap, non-hw
// char buf[ 320 * 240 * 3 ]; // this also works

// do NOT need to lock here, it's NOT 'in' a vid mem area 'yet'
fread( buf, 1, n, fp ); // fill up the buf

// Note : Pitch is a function of width and color depth, width is 320, depth is 24 bpp (3 bytes), 320 * 3 = 960
srcbmp = SDL_CreateRGBSurfaceFrom(buf, 320, 240, 24, 960,0,0,0,0 );

if( srcbmp->pixels != buf ) // check to make sure OUR buffer is used for the pixels
	printf("Creation used a COPY of our data, NOT our buffer!\r\n");

while ( 1 ) {
	//read data ( one frame of n bytes ) into buffer
	while ( SDL_PollEvent(&sdlevent) ) ;

	SDL_Delay ( 50 );				//50 ms per frame

	SDL_BlitSurface( srcbmp, NULL, screen, NULL );
	SDL_Flip(screen); // probably calls SDL_UpdateRect on WIn32, but on others it probably really flips the vid to hw memory

	if ( SDL_MUSTLOCK( srcbmp ) )
		SDL_LockSurface( srcbmp );

	// ALWAYS use the provided buffer, as we NOT guaranteed 'where' in memory it is!
	// IT should be buf pointer in Win32, but, on other platforms and depending upon hw, it's a 'maybe'
	if ( fread ( srcbmp->pixels, 1, n, fp )  <= 0 )
		break;
	if ( SDL_MUSTLOCK( srcbmp ) )
		SDL_UnlockSurface( srcbmp );

}  
SDL_FreeSurface( srcbmp );
free( buf );

} else {
while( 1) …

Worked just fine! (had to tweak the pitch as I didn’t understand it at first)
So, what was wrong?
I went back and reviewed the original code. I slept on it (from 2 to 9), and it finally hit me!
We filled the buffer we allocated, BUT that’s NOT where the pixel buffer was located for the DIB_bitmap!!!

I did a trace through the code and sure enough, the video->pixels were set up in the CreateDib call, but JUST before we went into the loop,
we had a little piece of erroneous code…

screen->pixels = buf;

this points the screen pixels buffer to our allocated buffer, BUT the DIB had set the buffer in the create call
We ‘can’t’ fill a different buffer and expect it to be rendered. And it’s NOT.

So comment out the assignment, and change the fread from

   if ( fread ( buf, 1, n, fp )  <= 0 )

to
if ( fread ( screen->pixels, 1, n, fp ) <= 0 )

Read INTO the alread created DIB pixels buffer!

Worked like a charm.
The War Craft short video is fairly nice in that small a format.

I’ll post the complete demo in a reply for easier access.

THanks SDL.org. This is NICE!
Looking forward to converting this to 1.3 for learning.

Enjoy

NOTE: The following code ‘attempts’ to follow the SDL portable guide lines, but was built with VisualStudio 2008 (VC 9) so uses a precompiled header requiring #include "stdafx.h"
comment that out for other platforms. Have not tested on anything but windows. Gotta learn how to run Ubuntu 8+ on Win7 virutal!

Code:

// SDLVplayer.cpp : Defines the entry point for the application.
//

#include “stdafx.h” // comment this out for NON-VisualC tools - can’t use #ifdef, stdafx.h MUST be first line (oh, bother me!)

#include “SDL.h”
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#pragma comment(lib, “SDL.lib”) // tell the linker we’ll need these
#pragma comment(lib, “SDLmain.lib”)

int main( int argc, char *argv[]) {
SDL_Surface *screen, *srcbmp;
SDL_Event sdlevent;
FILE *fp = NULL;
int n = 320 * 240 * 3; // 320 x 240 @ 24 bbp (24/8bperbyte) = 320 x 240 x 3

//hard-code file name and parameters just for demo purposes
if ( ( fp = fopen ( “sample_video.raw”, “rb” ) ) == NULL ) {
printf("\nError opening file \n");
exit ( 0 );
}
while ( SDL_PollEvent(&sdlevent) ) ;

//initialize video system
if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
fprintf(stderr, “Unable to init SDL: %s\n”, SDL_GetError());
exit(1);
}
//ensure SDL_Quit is called when the program exits
atexit(SDL_Quit);

//set video mode of 320 x 240 with 24-bit pixels
screen = SDL_SetVideoMode( 320, 240, 24, SDL_SWSURFACE);
if ( screen == NULL ) {
fprintf(stderr, “Unable to set 320x240 video: %s\n”, SDL_GetError());
exit(1);
}
while ( SDL_PollEvent(&sdlevent) ) ;

if ( argc > 1 && (strcmp(argv[1], “-blt”) == 0) ) {
//create our own in memory 'screen buffer - a DIB_Bitmap, non-hw
// char buf[ 320 * 240 * 3 ]; // this also works
char *buf = NULL;
//allocate memory to hold one frame of data
// n = 320 * 240 * 3;
buf = ( char *) malloc ( n ); // if we malloc it, we free it
assert ( buf ); //make sure memory has been allocated successfully

// do NOT need to lock here, it's NOT 'in' a vid mem area 'yet'
fread( buf, 1, n, fp ); // fill up the buf

// Note : Pitch is a function of width and color depth, width is 320, depth is 24 bpp (3 bytes), 320 * 3 = 960
srcbmp = SDL_CreateRGBSurfaceFrom(buf, 320, 240, 24, 960,0,0,0,0 );
if( srcbmp->pixels != buf )
	printf("Creation used a COPY of our data, NOT our buffer!\r\n");
while ( 1 ) {
	//read data ( one frame of n bytes ) into buffer
	while ( SDL_PollEvent(&sdlevent) ) ;
	SDL_Delay ( 50 );				//50 ms per frame
	SDL_BlitSurface( srcbmp, NULL, screen, NULL );
	SDL_Flip(screen); // probably calls SDL_UpdateRect on WIn32, but on others it probably really flips the vid to hw memory
	if ( SDL_MUSTLOCK( srcbmp ) )
		SDL_LockSurface( srcbmp );
	// ALWAYS use the provided buffer, as we NOT guaranteed 'where' in memory it is!
	// IT should be buf pointer in Win32, but, on other platforms and depending upon hw, it's a 'maybe'
	if ( fread ( srcbmp->pixels, 1, n, fp )  <= 0 )
		break;
	if ( SDL_MUSTLOCK( srcbmp ) )
		SDL_UnlockSurface( srcbmp );

}  
SDL_FreeSurface( srcbmp );
free( buf );

} else {
// default behavior, direct access to the screen->pixels
// NOTE we DON’T point our pixels to a different buffer, as it’s a REFERENCE NOT the original buffer inside the DIB
//screen->pixels = buf; //point framebuffer to data buffer
while ( 1 ) {

   while( SDL_PollEvent( &sdlevent ) );
   //read data ( one frame of n bytes ) into buffer	
   // USE the pixels buffer in the vid mem
   if ( fread ( screen->pixels, 1, n, fp )  <= 0 )
      break;
   SDL_Delay ( 50 );				//50 ms per frame
   SDL_UpdateRect ( screen, 0, 0, 0, 0 );	//blit data to screen

 }  

}

fclose ( fp );
SDL_Delay ( 2000 ); //delay 2 seconds before exit
printf(“Playing video successful!\n”);
return 0;
}