Logo

C# Topics

Using the erdmpg-6.dll file with C#

The Standard Mpeg Encoder offers the erdmpg-6.dll file as an alternative to the DirectShow filter. This dll file allows you to encode RGB24 video data and PCM audio data directly from memory into MPEG files (as well as all formats supported by the encoder.

There are two samples in the encoder package that show the usage of this dll:

  1. DirectAccessSample: This is a C++ console application that demonstrates how to load a bitmap file from disk into memory and create an MPEG file from this bitmap file. Additionally, a sound sample is generated in memory and saved in the same file
  2. CSharpDirectAccess: This is a C# sample that shows how to write encode all jpeg files in a particular directory into an MPEG file. The files are loaded into System.Drawing.Bitmap, and then the bit buffer is sent to the Dll to be encoded to an MPEG file.

Tutorial: Encode Bitmap into an MPEG file using C#

On a high level...

All you have to do is instanciate the MpegManager class, and pass in a System.Drawing.Bitmap image.

// Create a new MPEG File. Second parameter is the output format, 3rd and 4th
// parameters are the width and height
MpegManager mpegManager = new MpegManager("c:\\out.mpg", 1, 320, 240);

// Load the image into a bitmap
System.Drawing.Bitmap image = new System.Drawing.Bitmap("c:\\test.jpg");

// Write it as a frame
mpegManager.AddFrame(image);

// close the file
mpegManager.CloseFile();

Somewhat more detailed...

The MpegManager is a C# class that interfaces with the erdmpg-6.dll file. It does so over the the proxy StandardMpegEncoderDll.cs, which allows C# code interface with an unmanaged dll. This file should be included as a reference in your project, and contains the following functions:

int MediaFileSetBufferCallback(IntPtr pUserData, BufferCallback callbackFunc);
int MediaFileInitialize();
int MediaFileCreate();
int MediaFileClose();
int MediaFileWriteVideoRGB24(IntPtr imgDataP, int len, Int64 llStartTime, Int64 llEndTime);
int MediaFileWriteAudio(IntPtr audDataP, int len, Int64 llStartTime, Int64 llEndTime);
int MediaFileSetFormat(String pstrPresetName);
int MediaFileSetParameter(String pstrParameterName, String pstrParameterValue);
 

The functions above are the entire public interface for the dll. Those few functions allow you take full control over all aspects of your encoded file.

The general usage of the dll functions in this manner:

  1. You call the MediaFileInitialize function to get all the internal objects created
  2. You call the MediaFileSetFormat with a string preset like "mpeg-2" to have the parameters set to default values
  3. You use the function MediaFileSetParameter to tweak the parameters to fit your needs
  4. You call the function MediaFileSetBufferCallback to set the callback function that will receive the mpeg data as a binary stream. This function can write the file to disk or send the file over a network
  5. You call the function MediaFileCreate. This will create the Mpeg File header data using the loaded formats. After calling this function, your callback function will be called a few times with the data you need to write to create the header file
  6. You now retrieve your MPEG data, and write it using: MediaFileWriteVideoRGB24
  7. When you are done, call MediaFileClose

Simple Mpeg Encoding in C# - The MpegManager class


    class MpegManager
    {
        FileStream _fileStream;
        BinaryWriter _binaryWriter;
        int _expectedWidth;
        int _expectedHeight;
        const int ONE_SECOND = 10000000;
        long _curPicStart = 0;
        long _forcedPicDuration = 0; // 3 * ONE_SECOND; // change this to 0 to encode each frame normally

        public MpegManager(String fileName, int format, int sourceWidth, int sourceHeight) {
           
            // Reset the start position
            _curPicStart = 0;

            // sourceWidth and sourceHeight is the expected size of the bitmaps
            // If they do not match, they will be resized to that size
            _expectedWidth = sourceWidth;
            _expectedHeight = sourceHeight;

            // Open the output mpeg file. The data is sent in the callback at the
            // end of this file
            _fileStream = File.Create(fileName);
            _binaryWriter = new BinaryWriter(_fileStream);

            // Initialize the encoder
            StandardMpegEncoderDll.MediaFileInitialize();

            // Select the format (mpeg-1, etc)
            SelectFormat(format);

            // Set the source width and height
            StandardMpegEncoderDll.MediaFileSetParameter("nInputX", sourceWidth.ToString());
            StandardMpegEncoderDll.MediaFileSetParameter("nInputY", sourceHeight.ToString());

            // switch off audio
            StandardMpegEncoderDll.MediaFileSetParameter("bWriteAudioStream", "0");

            // Set the MPEG data callback function. If you wish, pass your
            // user specific data as the first parameter
            IntPtr myData = (IntPtr)0;
            BufferCallback cb = new BufferCallback(MpegBufferCallback);
            StandardMpegEncoderDll.MediaFileSetBufferCallback(myData, cb);

            // Create the file
            StandardMpegEncoderDll.MediaFileCreate();
           
        }

        public Bitmap ResizeBitmap(Bitmap b, int nWidth, int nHeight)
        {
            if (b.Width == nWidth && b.Height == nHeight)
                return b;

            Bitmap result = new Bitmap(nWidth, nHeight);
            using (Graphics g = Graphics.FromImage((Image)result))
                g.DrawImage(b, 0, 0, nWidth, nHeight);
            return result;
        }

        public bool AddFrame(System.Drawing.Bitmap bmp) {

            bmp = ResizeBitmap(bmp, _expectedWidth, _expectedHeight);

            Rectangle rc = new Rectangle(0, 0, bmp.Width, bmp.Height);

            // lock bitmap data
            BitmapData bmData = bmp.LockBits(rc, ImageLockMode.ReadWrite,
                                             PixelFormat.Format24bppRgb);

            // Get the address of the first line.
            IntPtr ptr = bmData.Scan0;

            int lenBitmap = bmp.Width * bmp.Height * 3;

            // Write to the mpeg file.
            if (_forcedPicDuration > 0) {
                StandardMpegEncoderDll.MediaFileWriteVideoRGB24(ptr, lenBitmap, _curPicStart, _curPicStart + _forcedPicDuration);
                _curPicStart += _forcedPicDuration;
            }
            else
                StandardMpegEncoderDll.MediaFileWriteVideoRGB24(ptr, lenBitmap, -1, -1);
 
            // unlock bitmap data
            bmp.UnlockBits(bmData);
            return true;
        }

        public void SelectFormat(int iFormatIndex) {
            // Presets are listed in VBGui
            switch (iFormatIndex)
            {
                case 0:
                    StandardMpegEncoderDll.MediaFileSetFormat("mpeg-1");
                    break;
                case 1:
                    StandardMpegEncoderDll.MediaFileSetFormat("mpeg-2");
                    break;
            }
        }

        public void CloseFile() {
            // Close the file
            StandardMpegEncoderDll.MediaFileClose();
        }

        Int64 MpegBufferCallback(IntPtr pUserData, IntPtr pDataBuffer, int bufferLen, int flags, Int64 seekPos, int nID)
        {
            // This is the function that receives the MPEG data. Write it to a file
            // or stream it. Be careful to seek to where the seekPos tells you to seek
            // to.

            switch (flags)
            {
                case 0: // open file
                    // file is already opened in constructor
                    break;
                case 1: // write file
                    byte[] managedBuffer = new byte[bufferLen];
                    Marshal.Copy(pDataBuffer, managedBuffer, 0, managedBuffer.Length);
                    _binaryWriter.Write(managedBuffer, 0, bufferLen);
                    break;
                case 2: // close
                    _binaryWriter.Close();
                    _fileStream.Close();
                    _binaryWriter = null;
                    _fileStream = null;
                    break;
                case 3: // seek
                    _binaryWriter.Seek((int)seekPos, SeekOrigin.Begin);
                    break;
            }

            return 0;
        }
    }