SharpAvi
SharpAvi copied to clipboard
support write BITMAPINFOHEADER extra data
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
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?
thank you for reply.
- 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.
- 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);
- For the size of the required memory, the
ICCompressGetFormatSize
macro (which is the same as theICCompressGetFormat
macro withlpbiOutput
set to 0) can be used to determine the maximum total size of theBITMAPINFOHEADER
+ 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 theICCompressGetFormat
macro is called.