SharpAvi icon indicating copy to clipboard operation
SharpAvi copied to clipboard

support write BITMAPINFOHEADER extra data

Open mes51 opened this issue 2 years ago • 2 comments

Sorry, I use machine translation.

Some codecs (e.g., Ut Video Codec, AMV4) add extra data after the BITMAPINFOHEADER obtained with the ICCompressGetFormat macro, and if this is not written to the AVI file, it will result in a broken file. However, AviWriter is currently generating the BITMAPINFOHEADER that it writes internally, which is different from the one obtained with the ICCompressGetFormat macro, so it cannot generate the correct AVI when using these codecs. Therefore, changes have been made to allow writing BITMAPINFOHEADER with extra data attached. To maintain compatibility, I have added a new interface instead of adding properties to IVideoEncoder.

see: ffmpeg extracting BITMAPINFOHEADER extradata code https://github.com/FFmpeg/FFmpeg/blob/a2564264116aabc5a95755ca93a31508ef547f40/libavformat/avidec.c#L785

mes51 avatar Nov 27 '22 11:11 mes51

Hi @mes51, thanks for your contribution. I want to clarify some things about your use case.

  • Do you employ your own implementation of IVideoEncoder for mentioned codecs?
  • If so, could you use the built-in 'Mpeg4VcmVideoEncoder' if it handled getting that extra data from the codec?
  • How do you know how much memory should you provide for extra data when calling the ICCompressGetFormat macro? And whether all necessary data were written into the provided memory after the call?

baSSiLL avatar Dec 17 '22 12:12 baSSiLL

thank you for reply.

  1. Yes. However, for my use case, I need the user to be able to choose from installed codecs on his computer, rather than a specific codec, so I implemented an IVideoEncoder that takes FourCC of any codec and does the encoding.
  2. Since it includes processing for my application, it is difficult to divert it to Mpeg4VcmVideoEncoder. However, the header getting process can be implemented as the pseudo code below.
// in VfwApi class
public const int ICM_COMPRESS_GET_FORMAT = 0x4004;

// for ICCompressGetFormat macro & ICCompressGetFormatSize macro
[DllImport(VFW_DLL, CallingConvention = CallingConvention.Winapi)]
public static extern int ICSendMessage(IntPtr handle, int message, ref BitmapInfoHeader inHeader, IntPtr outHeader);

// in Mpeg4VcmVideoEncoder class
static byte[] GetOutputHeader(IntPtr compressorHandle, VfwApi.BitmapInfoHeader inBitmapInfo)
{
    var inHeader = inBitmapInfo;
    var headerSize = VfwApi.ICSendMessage(compressorHandle, VfwApi.ICM_COMPRESS_GET_FORMAT, ref inHeader, IntPtr.Zero); // ICCompressGetFormatSize macro
    if (headerSize < 1)
    {
        return null;
    }

    var outputBitmapInfoHeader = new byte[headerSize];
    var pin = GCHandle.Alloc(outputBitmapInfoHeader);
    var result = VfwApi.ICSendMessage(compressorHandle, VfwApi.ICM_COMPRESS_GET_FORMAT, ref inHeader, Marshal.UnsafeAddrOfPinnedArrayElement(outputBitmapInfoHeader, 0)); // ICCompressGetFormat macro
    pin.Free();
    
    if (result == VfwApi.ICERR_OK)
    {
        return outputBitmapInfoHeader;
    }
    else
    {
        return null;
    }
}

// in Mpeg4VcmVideoEncoder constructor
if (compressorHandle == IntPtr.Zero)
{
    throw new InvalidOperationException("No compatible MPEG-4 encoder found.");
}
BitmapInfoHeader = GetOutputHeader(compressorHandle, inBitmapInfo);
  1. For the size of the required memory, the ICCompressGetFormatSize macro (which is the same as the ICCompressGetFormat macro with lpbiOutput set to 0) can be used to determine the maximum total size of the BITMAPINFOHEADER + extra data.
    https://learn.microsoft.com/en-us/windows/win32/api/vfw/nf-vfw-iccompressgetformatsize
    https://learn.microsoft.com/en-us/windows/win32/api/vfw/nf-vfw-iccompressgetformat
    I currently can't find any codecs where the extra data changes during encoding, and it seems that all the extra data needed for decoding can be retrieved at the time the ICCompressGetFormat macro is called.

mes51 avatar Dec 18 '22 08:12 mes51