diff --git a/SharpAvi/Codecs/IVideoEncoderExtraData.cs b/SharpAvi/Codecs/IVideoEncoderExtraData.cs new file mode 100644 index 0000000..a3d9326 --- /dev/null +++ b/SharpAvi/Codecs/IVideoEncoderExtraData.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SharpAvi.Codecs +{ + /// + /// Export extra data for some codecs + /// + public interface IVideoEncoderExtraData + { + /// + /// Encoded images header with extra data + /// + byte[] BitmapInfoHeader { get; } + } +} diff --git a/SharpAvi/Codecs/SingleThreadedVideoEncoderWrapper.cs b/SharpAvi/Codecs/SingleThreadedVideoEncoderWrapper.cs index d4305da..a662f1f 100644 --- a/SharpAvi/Codecs/SingleThreadedVideoEncoderWrapper.cs +++ b/SharpAvi/Codecs/SingleThreadedVideoEncoderWrapper.cs @@ -14,7 +14,7 @@ namespace SharpAvi.Codecs /// like asynchronous encoding. /// /// - public class SingleThreadedVideoEncoderWrapper : IVideoEncoder, IDisposable + public class SingleThreadedVideoEncoderWrapper : IVideoEncoder, IVideoEncoderExtraData, IDisposable { private readonly IVideoEncoder encoder; private readonly SingleThreadTaskScheduler scheduler; @@ -65,6 +65,21 @@ public void Dispose() /// public int MaxEncodedSize => SchedulerInvoke(() => encoder.MaxEncodedSize); + /// + /// Encoded images header + /// + public byte[] BitmapInfoHeader => SchedulerInvoke(() => + { + if (encoder is IVideoEncoderExtraData extraData) + { + return extraData.BitmapInfoHeader; + } + else + { + return null; + } + }); + /// /// Encodes a video frame. /// diff --git a/SharpAvi/Output/AviVideoStream.cs b/SharpAvi/Output/AviVideoStream.cs index a749bb3..9384330 100644 --- a/SharpAvi/Output/AviVideoStream.cs +++ b/SharpAvi/Output/AviVideoStream.cs @@ -13,6 +13,7 @@ internal class AviVideoStream : AviStreamBase, IAviVideoStreamInternal private int height; private BitsPerPixel bitsPerPixel; private int framesWritten; + private byte[] bitmapInfoHeader; public AviVideoStream(int index, IAviStreamWriteHandler writeHandler, int width, int height, BitsPerPixel bitsPerPixel) @@ -71,6 +72,16 @@ public FourCC Codec } } + public byte[] BitmapInfoHeader + { + get { return bitmapInfoHeader; } + set + { + CheckNotFrozen(); + bitmapInfoHeader = value; + } + } + public void WriteFrame(bool isKeyFrame, byte[] frameData, int startIndex, int count) { Argument.IsNotNull(frameData, nameof(frameData)); diff --git a/SharpAvi/Output/AviWriter.cs b/SharpAvi/Output/AviWriter.cs index ead4b69..9f7d24a 100644 --- a/SharpAvi/Output/AviWriter.cs +++ b/SharpAvi/Output/AviWriter.cs @@ -537,39 +537,47 @@ void IAviStreamWriteHandler.WriteStreamHeader(AviAudioStream audioStream) void IAviStreamWriteHandler.WriteStreamFormat(AviVideoStream videoStream) { - // See BITMAPINFOHEADER structure - fileWriter.Write(40U); // size of structure - fileWriter.Write(videoStream.Width); - fileWriter.Write(videoStream.Height); - fileWriter.Write((short)1); // planes - fileWriter.Write((ushort)videoStream.BitsPerPixel); // bits per pixel - fileWriter.Write((uint)videoStream.Codec); // compression (codec FOURCC) - // 0 size is safer for uncompressed formats not to bother with stride calculation - var sizeInBytes = videoStream.Codec == CodecIds.Uncompressed - ? 0 - : videoStream.Width * videoStream.Height * (((int)videoStream.BitsPerPixel) / 8); - fileWriter.Write((uint)sizeInBytes); // image size in bytes - fileWriter.Write(0); // X pixels per meter - fileWriter.Write(0); // Y pixels per meter - - // Writing grayscale palette for 8-bit uncompressed stream - // Otherwise, no palette - if (videoStream.BitsPerPixel == BitsPerPixel.Bpp8 && videoStream.Codec == CodecIds.Uncompressed) + var bitmapInfoHeader = videoStream.BitmapInfoHeader; + if (bitmapInfoHeader != null) { - fileWriter.Write(256U); // palette colors used - fileWriter.Write(0U); // palette colors important - for (int i = 0; i < 256; i++) - { - fileWriter.Write((byte)i); - fileWriter.Write((byte)i); - fileWriter.Write((byte)i); - fileWriter.Write((byte)0); - } + fileWriter.Write(bitmapInfoHeader); } else { - fileWriter.Write(0U); // palette colors used - fileWriter.Write(0U); // palette colors important + // See BITMAPINFOHEADER structure + fileWriter.Write(40U); // size of structure + fileWriter.Write(videoStream.Width); + fileWriter.Write(videoStream.Height); + fileWriter.Write((short)1); // planes + fileWriter.Write((ushort)videoStream.BitsPerPixel); // bits per pixel + fileWriter.Write((uint)videoStream.Codec); // compression (codec FOURCC) + // 0 size is safer for uncompressed formats not to bother with stride calculation + var sizeInBytes = videoStream.Codec == CodecIds.Uncompressed + ? 0 + : videoStream.Width * videoStream.Height * (((int)videoStream.BitsPerPixel) / 8); + fileWriter.Write((uint)sizeInBytes); // image size in bytes + fileWriter.Write(0); // X pixels per meter + fileWriter.Write(0); // Y pixels per meter + + // Writing grayscale palette for 8-bit uncompressed stream + // Otherwise, no palette + if (videoStream.BitsPerPixel == BitsPerPixel.Bpp8 && videoStream.Codec == CodecIds.Uncompressed) + { + fileWriter.Write(256U); // palette colors used + fileWriter.Write(0U); // palette colors important + for (int i = 0; i < 256; i++) + { + fileWriter.Write((byte)i); + fileWriter.Write((byte)i); + fileWriter.Write((byte)i); + fileWriter.Write((byte)0); + } + } + else + { + fileWriter.Write(0U); // palette colors used + fileWriter.Write(0U); // palette colors important + } } } diff --git a/SharpAvi/Output/EncodingVideoStreamWrapper.cs b/SharpAvi/Output/EncodingVideoStreamWrapper.cs index 2414f2a..6fd843d 100644 --- a/SharpAvi/Output/EncodingVideoStreamWrapper.cs +++ b/SharpAvi/Output/EncodingVideoStreamWrapper.cs @@ -56,6 +56,22 @@ public override BitsPerPixel BitsPerPixel set => ThrowPropertyDefinedByEncoder(); } + public override byte[] BitmapInfoHeader + { + get + { + if (encoder is IVideoEncoderExtraData extraData) + { + return extraData.BitmapInfoHeader; + } + else + { + return BaseStream.BitmapInfoHeader; + } + } + set => ThrowPropertyDefinedByEncoder(); + } + /// Encodes and writes a frame. public override void WriteFrame(bool isKeyFrame, byte[] frameData, int startIndex, int length) { @@ -94,6 +110,10 @@ public override void PrepareForWriting() // Set properties of the base stream BaseStream.Codec = encoder.Codec; BaseStream.BitsPerPixel = encoder.BitsPerPixel; + if (encoder is IVideoEncoderExtraData extraData) + { + BaseStream.BitmapInfoHeader = extraData.BitmapInfoHeader; + } base.PrepareForWriting(); } diff --git a/SharpAvi/Output/IAviVideoStreamInternal.cs b/SharpAvi/Output/IAviVideoStreamInternal.cs index 9450af3..359b6aa 100644 --- a/SharpAvi/Output/IAviVideoStreamInternal.cs +++ b/SharpAvi/Output/IAviVideoStreamInternal.cs @@ -2,5 +2,6 @@ { internal interface IAviVideoStreamInternal : IAviVideoStream, IAviStreamInternal { + byte[] BitmapInfoHeader { get; set; } } } diff --git a/SharpAvi/Output/VideoStreamWrapperBase.cs b/SharpAvi/Output/VideoStreamWrapperBase.cs index 9813d99..1e043dc 100644 --- a/SharpAvi/Output/VideoStreamWrapperBase.cs +++ b/SharpAvi/Output/VideoStreamWrapperBase.cs @@ -47,6 +47,12 @@ public virtual FourCC Codec set { BaseStream.Codec = value; } } + public virtual byte[] BitmapInfoHeader + { + get { return BaseStream.BitmapInfoHeader; } + set { BaseStream.BitmapInfoHeader = value; } + } + public virtual void WriteFrame(bool isKeyFrame, byte[] frameData, int startIndex, int length) { Argument.IsNotNull(frameData, nameof(frameData));