Search code examples
c#videopngaforgeavi

What's the proper frame rate used to convert png files to a video file?


Every 1000 / 60fps milliseconds I take screenshot of a window and save as a png file to a directory then I use VideoFileWriter to create a AVI file from those images. But I don't how frame rate should I use. How do I define it? it's based on width or height or what else? I tried copy the default frame rate used by virtualdub, I found it to be 1000.. I tried that rate but the video output was redicuosly slow. Some doc examples use 25. I don't really know how define that.

            const int width = 1920;
            const int height = 1080;
            const int frameRate = 60;

using (var writer = new VideoFileWriter())
            {
                writer.Open("output.avi", width, height, frameRate, VideoCodec.MPEG4);

                foreach (string file in Directory.EnumerateFiles(dest_path, "*.png"))
                {
                    using (var img = (Bitmap)Image.FromFile(file))
                    {
                        writer.WriteVideoFrame(img);
                    }
                }
            }

Code used to take the screnshots:

int i = 0;
        void doRecord()
        {  
            using (var img = GetScreenshot(handle))
            {
                string filename = Path.Combine(dest_path, string.Format("{0}.png", ++i));
                img.Save(filename, ImageFormat.Png);
            }
        }

        public Bitmap GetScreenshot(IntPtr hwnd)
        {
            RECT rc;

            if (!GetWindowRect(hwnd, out rc))
                throw new Win32Exception(Marshal.GetLastWin32Error());

            Bitmap bmp = new Bitmap(rc.right - rc.left, rc.bottom - rc.top, PixelFormat.Format32bppArgb);
            using (var gfxBmp = Graphics.FromImage(bmp))
            {
                IntPtr hdcBitmap = gfxBmp.GetHdc();
                bool succeeded = PrintWindow(hwnd, hdcBitmap, 0);
                gfxBmp.ReleaseHdc(hdcBitmap);
                if (!succeeded)
                {
                    gfxBmp.FillRectangle(new SolidBrush(Color.Gray), new Rectangle(Point.Empty, bmp.Size));
                }
                IntPtr hRgn = CreateRectRgn(0, 0, 0, 0);
                GetWindowRgn(hwnd, hRgn);
                Region region = Region.FromHrgn(hRgn);
                if (!region.IsEmpty(gfxBmp))
                {
                    gfxBmp.ExcludeClip(region);
                    gfxBmp.Clear(Color.Transparent);
                }
                return bmp;
            }
        }

I use a Timer to take the screenshot, set up like this:

const int fps = 60;
timer1.Interval = 1000 / fps;
timer1.Start();

and

private void timer1_Tick(object sender, EventArgs e)
{
   doRecord();
}

Solution

  • There is absolutely no correlation between the shape and/or size of the images you take and the frame rate.

    Short answer:

    You need to calculate how many screenshots you take every second and make that number your frame rate.

    Long Answer:

    I think you are missing on what frame rate means, I'll explain: Human eyes (as most animals) have a property called "Persistence of Vision" which basically allows us to interpret the movement of objects as a continuous moving image. This is the concept on which all videos are based.

    If you take a bunch photos of a running person and then pass them very fast in front of your eyes you will see the person moving. But it wouldn't necessarily take the same time as it took the actual person. Let's put some numbers in here for context.

    You ask your friend Steve to run 1 km (about half a mile) and you start taking lots of photos of him running. Actually, you take 60 photos each second. Now the average person runs at 10 km/h so let's use that running speed. It would take Steve 1/10 of an hour to run 1 km, because there are 3600 seconds in 1 hour that would be the same as 360 seconds or 6 minutes. So you have taken exactly (60*360) 21600 photos.

    Data recap:

    • Length run: 1 km
    • Speed: 10km/h
    • Time: 360 seconds
    • Photos per second: 60
    • Total photos: 21600

    Now, if you wanted to reproduce the photos so it took you the same time to see them as it took Steve to run that distance you would have to display every second the 60 photos you took in that second. So you would end up showing 60 photos per second. When you make a video you refer to each of these photos as a "frame", therefore you are using a 60 frames/second frame rate (or fps).

    If you wanted to show a slow-motion video of Steve running you need to lower the amount of fps, going down to 30 fps for half the speed. That would let you with a 12 minute long slow-motion video.

    Please keep in mind that videos usually have over 24 fps, and the lower the fps the more "lagged" the video will look

    Note: I seriously don't recommend using that method for screen/window recording since it will result in a huge waste of the computer's resources (CPU, RAM and Hard Disk). You should look up some libraries that already do all recording and patching up for you. Unfortunately, I don't have any to recommend.