Link to home
Start Free TrialLog in
Avatar of dhyanesh
dhyanesh

asked on

AVI frame split using C or C++

Hi

I wish to split an AVI file into individual frames. I would like to use these frames and later store in a BMP file.

I have the AVI file format from

http://www.wotsit.org

I can get all the header info etc.

The problem is the main data part. The format says that the file contains DIB in compressed and/or uncompressed format starting with 'dc' and 'db' respectively. I tried to get those parts but the image does not come out proper.

I also noticed that the total no. of 'db's and 'dc's in the file are twice the no. of frames.

I am working with gcc on Linux platform. This is part of my project.

Dhyanesh

Avatar of grg99
grg99

Try printing out the info you're getting and see if the values in the struct make sense.   Sometimes there are unexpected header bloks that have to be processed or skipped.  Also if you're on a non-Intel-endian machine, like a PowerPC, you'll have to re-endian data.




Avatar of dhyanesh

ASKER

I have got all the headers correctly. i.e. I get the total no of frames, frame rate, height, width etc. all ok.

The problem is after the headers the actual data is stored in DIB BMP format. I also got the BMP headers perfect. All the specifications match.

After these headers there is the palette which also I got. Then the actual data is stored. 'db' represents the start of uncompressed data. 'dc' represents start of compressed data. I do not know when to stop. I tried from one 'db' to another 'db' or 'dc' but it wont work. This way I get only part of the image. Part is garbled and some part is completely not there. Also I selected a clip which does not have any audio.

One thing I noticed as I have posted above is "total no. of 'db's and 'dc's in the file are twice the no. of frames"

I am on an Intel machine

Dhyanesh
in bitmapinfoheader you have field biSizeImage that Specifies the size, in bytes, of the image, so you have to read only biSizeImage bytes from file to get the image.
RomanPetrenko,

I tried what you are saying but in the avi file the images are stored after a 'db' or 'dc'. The no. of bytes read in between is much less than that. I also tried reading exactly biSizeImage bytes from file. Still I  am unable to get a correct BMP

Dhyanesh
What program did you use for creating avi? Can you give me a link to this avi?
Avatar of Narendra Kumar S S
If there is a starting point for db/dc, then there must be a ending point also!
I think you have missed something!

-ssnkumar
typedef struct tagBITMAPINFOHEADER {
...
   DWORD biCompression;
   DWORD biSizeImage;
...
} BITMAPINFOHEADER;
The above structure is used for AVI header format. In this format, "biSizeImage" will give the size of the data compressed in the format defined by biCompression.

This is what one of the manual about the format says:
"Your driver can define custom compression and bitmap formats by assigning a four-character code to the biCompression field in place of the standard constants. When you define a custom format, you must specify the number of bytes in the image in the biSizeImage field. "

Hope this will help....

-ssnkumar
RomanPetrenko,

I did not create the avi file. I just picked up an avi file and tried to get its frame. The ones I tested were pretty small about 10KB. You might be having them if you have Visual Studio. They are "filedisk.avi" and "filecopy.avi" under

"C:\Program Files\InstallShield\InstallShield for Microsoft Visual C++\Program"



ssnkumar,

>If there is a starting point for db/dc, then there must be a ending point also!

That is exactly what I feel. However in the file format it is not mentioned anywhere about ending of the frame!

>biSizeImage

I have tried reading exactly that but in between two 'db's or 'dc's the no. of bytes are much less than biSizeImage. I also tried reading until I get biSizeimage still I do not get correct bmp.

Dhyanesh

Be sure you're opening and reading the file in a BINARY mode, not a ASCII mode.

ASCII mode is going to goof up the byte count, as it will mangle any \r or ^Z bytes in the bitmaps.

grg99,

I have read the file in binary mode.

The different operations with the file I have used are:

infile.open("filedisk.avi",ios::binary|ios::in);

infile.read((char *)&bmph,40);

infile >> ch;

outfile.open("testnew.bmp",ios::binary|ios::out);

outfile.write((char *)&bmpf,14);

Should I post my code here?

It is not very well written and not commented since it is just some sort of an initial draft. Anyway I can post my code.

Dhyanesh
That would help if yoou posted the code.  It may be some little oversight.
#include <iostream.h>
#include <fstream.h>
#include <string.h>
#include <stdlib.h>


typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;

/*RIFF ('AVI '
       LIST ('hdrl'
                   'avih'(<Main AVI Header>)
                   LIST ('strl'
                   'strh'(<Stream header>)
                   'strf'(<Stream format>)
                   'strd'(additional header data)
                       .
                       .
                       .
             )
                          .
                   .
             .
       )

LIST ('movi'
                   {SubChunk | LIST('rec '
                              SubChunk1
                              SubChunk2
                                .
                               .
                                 .
                         )

                 .
                 .
                 .
             }

                        .
                        .
                        .
   )

       ['idx1'<AVIIndex>]
)*/





typedef struct {                     //The Main AVI Header
            DWORD  dwMicroSecPerFrame;
            DWORD  dwMaxBytesPerSec;
            DWORD  dwReserved1;
            DWORD  dwFlags;
            DWORD  dwTotalFrames;
            DWORD  dwInitialFrames;
            DWORD  dwStreams;
            DWORD  dwSuggestedBufferSize;
            DWORD  dwWidth;
            DWORD  dwHeight;
            DWORD  dwScale;
            DWORD  dwRate;
            DWORD  dwStart;
            DWORD  dwLength;
} MainAVIHeader;


typedef struct {                          //AVI Stream Header
            char    fccType[4];
            char    fccHandler[4];
            DWORD   dwFlags;
            DWORD   dwReserved1;
            DWORD   dwInitialFrames;
            DWORD   dwScale;
            DWORD   dwRate;
            DWORD   dwStart;
            DWORD   dwLength;
            DWORD   dwSuggestedBufferSize;
            DWORD   dwQuality;
            DWORD   dwSampleSize;
} AVIStreamHeader;


/*WAVE  Bytes   '##wb'
                  BYTE    abBytes[];*/

/*DIB  Bits   '##db'
             BYTE   abBits[];*/

typedef struct {
            BYTE          bFirstEntry;
            BYTE          bNumEntries;
            WORD          wFlags;
            BYTE                  *peNew;
} AVIPALCHANGE;

typedef struct {
            DWORD  ckid;
            DWORD  dwFlags;
            DWORD  dwChunkOffset;
            DWORD  dwChunkLength;
} AVIINDEXENTRY;

typedef struct BMPFILEHEADER {
 char type[2];
 DWORD size;
 DWORD reserved;
 DWORD offset;
}FileHeader;


typedef struct BMPINFOHEADER {
 DWORD headersize;
 DWORD width;
 DWORD height;
 WORD planes;
 WORD bitsperpixel;
 DWORD compression;
 DWORD sizeimage;
 DWORD xpixelspermeter;
 DWORD ypixelspermeter;
 DWORD colorsused;
 DWORD colorsimportant;
} BmpHeader;



typedef struct tagBITMAP {
 DWORD width;
 DWORD height;
 BYTE *data;
}BITMAP;




int main(void)
{
      ifstream infile;
      ofstream outfile;
      infile.open("filedisk.avi",ios::binary|ios::in);
      outfile.open("testnew.bmp",ios::binary|ios::out);
      if (!infile)
            cout << "Error opening file";
//      DWORD x;

      FileHeader bmpf;
      bmpf.type[0] = 'B';
      bmpf.type[1] = 'M';
      bmpf.reserved = 0;


// DWORD size;

// DWORD offset;

      BmpHeader bmph;
      BITMAP bitmap;
      char palette[768];
      char ch,che[4];
      int i=0,count;
      int imageno = 1;
      int skip = 0;                    //This is no. of fields delimited by 'db' and/or 'dc' to skip
      int x=0,y=261;
      MainAVIHeader avih;
      while (!infile.eof())
      {
                  infile >> ch;
                  if (ch == 'a')
                  {
                        infile >> che[0];
                        infile >> che[1];
                        infile >> che[2];
                        che[3] = '\0';
                        if (!strcmp(che,"vih"))
                        {
                              infile.ignore(4);
                              infile.read((char *)&avih,14*4);
                              break;
                        }
                  }
      }
      while (!infile.eof())
      {
      infile >> ch;
      if (ch == 'v')
      {
            infile >> che[0];
                       infile >> che[1];
            infile >> che[2];
            che[3] = '\0';
            if (!strcmp(che,"ids"))
            {
               while(1)
            {
                infile >> ch;
                if (ch == 's')
               {
                  infile >> che[0];
                  infile >> che[1];
                  infile >> che[2];
                  che[3] = '\0';
                  if (!strcmp(che,"trf"))
                  {
                                  infile.ignore(4);
                        infile.read((char *)&bmph,40);
//                        cout << bmph.bitsperpixel;
                        if (bmph.bitsperpixel != 24)
                        {
                   /***read palette information***/
                         for(i=0;i<256;i++)
                         {
                                         palette[i*3+2]=infile.get()>>2;
                               palette[i*3+1]=infile.get()>>2;
                               palette[i*3+0]=infile.get()>>2;
                               infile.get();
                         }
                                         }
                                          bitmap.width=bmph.width;
                                     bitmap.height=bmph.height;
                                   bitmap.data=new BYTE[bitmap.width*bitmap.height];
                         count = bitmap.width*bitmap.height;
                         while(1)
                        {
                                infile >> ch;
                                                                   if (ch == '0')
                               {
                              infile >> che[0];
                              infile >> che[1];
                                        infile >> che[2];
                              che[3] = '\0';
                              if (!strcmp(che,"0db") || !strcmp(che,"0dc"))
                              {
                                        imageno--;
                                                        if (imageno == 0)
                                      {
                                     infile >> bitmap.data[i++];
                                       while (1)
                                     {
                                                              infile >> ch;
                                          if (ch == '0')
                                          {
                                                            infile >> che[0];
                                                                 infile >> che[1];
                                                          infile >> che[2];
                                                che[3] = '\0';
                                                                    if (!strcmp(che,"0db") || !strcmp(che,"0dc"))
                                                {
                                                skip--;
                                                                     if (skip == -1)
                                                {
                                                count = i;
                                                bmpf.size = i;
                                                break;
                                                }
                                          }
                                          else
                                          {
                                                bitmap.data[i++] = ch;
                                                bitmap.data[i++] = che[0];
                                                bitmap.data[i++] = che[1];
                                                bitmap.data[i++] = che[2];
                                                if (i > bmph.sizeimage)
                                                      break;
                                          }
                                                         }
                                                         else
                                    {
                                    bitmap.data[i++] = ch;
                                    if (i > bmph.sizeimage)
                                                           break;
                              }
                         }
                                            break;
                  }
            }
        }
         }
         break;
     }
  }
}
}
}
//            infile.read((char *)&x,4);
//            outfile.write((char *)&x,4);
      }
      bmpf.offset = 54;
      bmpf.size += 54;
      if (bmph.bitsperpixel != 24)
      {
            bmpf.offset += 1024;
            bmpf.size += 1024;
      }
      outfile.write((char *)&bmpf,14);
      outfile.write((char *)&bmph,40);

      for(i=0;i<256;i++)
      {
            outfile.put(palette[i*3+2]<<2);
            outfile.put(palette[i*3+1]<<2);
            outfile.put(palette[i*3+0]<<2);
            outfile.put(0);
      }



      outfile.write(bitmap.data,count);
      delete bitmap.data;

      outfile.close();
      infile.close();
      getch();
      closegraph();
      return 0;
}
Really sorry for the indenting quite bad.

Also the logic I used is not very elegant. At present I am only trying to understand the AVI file format.

Dhyanesh

please remove

closegraph();

getch();

from the end of the code.

I was actually trying out the code on Turbo C++ but will later port it to gcc

Dhyanesh
You might check whether your structs are getting the right packing and alignment.  You might need a #pragma pack(1) to get things lined up straight.

What I usually do in a case like this is to "tidy up" the code. Almost always find the problem this way:


#1: comment each loop and if.  It would be nice to see comments like "getting bitmap info", "reading bitmap", etc.

#2:  Add calls to a Bomb() function for impossible or unexpected input.  It would be nice to see lines like: Bomb("Not a db");
Bomb("Impossible dimension"); Bomb("Impossible length");

-----------------

A good 88% of the time I find the problem just by following these practices.  Someday I will catch on and write code like this the FIRST time and avoid a lot of headaches.

>>I have tried reading exactly that but in between two 'db's or 'dc's the no. of bytes are much less than biSizeImage. I also tried reading until I get biSizeimage still I do not get correct bmp.

Don't you think biSizeImage includes the size of header!? Looking at your comments, I think you have not taken into consideration the header as part of data.
So, do try by taking size of header also into consideration....

-ssnkumar
ssnkumar

I do not think biSizeimage contains the size of the header. The header of a bitmap is given only ONCE in the avi file and this header is used for all.

Even if biSizeimage contains the header the frame is still a long way from being correct.

Dhyanesh
grg99, I am getting the structs proper. The problem is with the data part.

I agree with grg99 that the code I am using is pretty bad and not very well commented. I will re code it but after a while.

To all
---------

However I still feel there is something BIG that is amiss. This is because when I opened the avi file in notepad and read its contents, towards the end I saw the 'dc's placed closer to each other than in the beggining of the file. It might be only 8 - 10 bytes between 2 'dc's whereas the image size is 20,880 i.e. biSizeimage. This is not just for a single avi. I opened 5 - 6 files and found the same pattern.

Also the pattern of total no. of 'db' + 'dc' = 2 * no of frames was true for all the avi files.

Dhyanesh
Can anybody tell me where I can get code in C for existing AVI players? I am sure there must be some open source Linux AVI player
ASKER CERTIFIED SOLUTION
Avatar of RomanPetrenko
RomanPetrenko

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
RomanPetrenko,

Your program works great for small files like filecopy and filedisk.

However for larger files all the frames are blank. Is the size a reason or do they have a different format ?

One more thing. When the files filecopy and filedisk are split to frames the first frame gives the entire picture. The next frames give only the difference or something like that. What type of compression is this? Is is motion estimation and compesation?

Dhyanesh
They have different format I think. For larger files used some kind of codecs...(DivX, XviD and so on). so you can't extract bitmaps directly from AVI.
this is the clasic schema how video Player works...

FileParser->Codec->Frames->Monitor(BMP) and there io no codecs in my prog FileParser->Frames->Monitor. So youcan work only with "original" avi(created without codecs).

Is it motion estimation? I'm not sure. This is like frames stored in avi.

>> This is like frames stored in avi.


If you extract the frames in filecopy.avi, you will notice that the first frame has a purple background. However the remaining frames do not have a purple background. They have a black background and only the page which is moving is redrawn. The rest of the image is black. It seems that somehow this frame is mixed with the previous frame to get current frame. How do I know which of AVI files use such a technique? This will be very crucial


Dhyanesh
in idx1 chunk stored following structure:
typedef struct _avioldindex {
   FOURCC  fcc;
   DWORD   cb;
   struct _avioldindex_entry {
      DWORD   dwChunkId;
      DWORD   dwFlags;
      DWORD   dwOffset;
      DWORD   dwSize;
  } aIndex[];
} AVIOLDINDEX;

where dwFlags can contain AVIIF_KEYFRAME which mean that this frame is full picture, frames between key frames contains just difference images.
AVIMAINHEADER also contains dwFlags that can be AVIF_HASINDEX - means that avi file contain idx1 chunk and AVIF_MUSTUSEINDEX - which means that you should use index instead of physical ordering of chunks.

look this three links:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/directx/htm/avioldindexstructure.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/directx/htm/avirifffilereference.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/directx/htm/avimainheaderstructure.asp

Thanks a lot  RomanPetrenko i will look into this.

All my problems are solved for now.
One more question i have.

How do I know whether a file uses a codec or not.

Dhyanesh
Does fccHandler give which codec is used.

Also where can i get info for all the codecs

Dhyanesh
fccHandler contain code of codec... (DivX XVid and so on)
I don't know where in unix stored this infromation but for windows in registry HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Drivers32
values that started from vidc. is codecs.


Thanks a lot
I am sure someone can help here as well:(especially RomanPetrenko)

https://www.experts-exchange.com/questions/20943050/AVI-creation-from-frames.html

It is about AVI files

Dhyanesh