11# H264Sharp
22Cisco's OpenH264 Native wrapper for .Net with optimised image format conversion support. It is very suitable for realtime streaming over network.
3+ This is the only open source C# library with full feature wrapper.
4+ Image format converters are faster than OpenCV implementation.
35- Plug&Play
46- Tested on .NetFramework and Net(up to 8).
57- Compatible with OpenCV.(i.e. OpenCVsharp)
6- - Tested on WPF application with camera and screen capture (P2P Videocall) .
7- - No memory leaks or GC pressure with bitmaps .
8- - Simple console application example is provided as an example.
8+ - Tested on WPF application with camera and screen capture.
9+ - No memory leaks or GC pressure.
10+ - Simple console application example and WPF application is provided as an example.
911
1012Library consist of native dll which acts as OpenH264 wrapper and image format converter (YUV420p <-> RGB,BGR,RGBA,BGRA)
11- <br />Converters are vectorised(AVX2) for high performance.
13+ <br />Converters are vectorised(AVX2 and SSE) and can be configured for parallelisation for high performance.
1214
1315C# library is .Net standard wrapper library for this dll and performs PInvoke to handle transcoding.
1416## Nuget
1517Install the nuget package and its ready to go. All native dependencies are automatically installed and will apepear on your executable directory.
1618
17- [ ![ NuGet] ( https://img.shields.io/nuget/v/H264Sharp )] ( https://www.nuget.org/packages/H264Sharp/1.0.4 )
19+ [ ![ NuGet] ( https://img.shields.io/nuget/v/H264Sharp )] ( https://www.nuget.org/packages/H264Sharp/1.2.0 )
1820
1921For usage in Unity, You have to specify the absolute path for openh264 dll. (i.e. StreamingAssets)
2022``` c#
@@ -26,52 +28,59 @@ Examples can be found on examples directroy.
2628
2729Following code shows encoder and decoder in action, commented lines are for hints.
2830``` c#
31+
2932static void Main (string [] args )
3033{
31- Encoder encoder = new Encoder ();
32- Decoder decoder = new Decoder ();
33-
34- var img = System .Drawing .Image .FromFile (" ocean .jpg" );
35- int w = img .Width ;
34+ H264Encoder encoder = new H264Encoder ();
35+ H264Decoder decoder = new H264Decoder ();
36+
37+ var img = System .Drawing .Image .FromFile (" ocean1080 .jpg" );
38+ int w = img .Width ;
3639 int h = img .Height ;
3740 var bmp = new Bitmap (img );
3841
39- encoder .Initialize (w , h , bps :20_ 000_ 000 , fps :30 , ConfigType .CameraBasic );
40-
41- for (int j = 0 ; j < 100 ; j ++ )
42- {
43- var data = BitmapToGenericImage (bmp );
44- encoder .Encode (data , out EncodedData [] ec );
42+ encoder .Initialize (w , h , bps :10_ 000_ 000 , fps :30 , ConfigType .CameraBasic );
43+ decoder .Initialize ();
4544
46- // encoder.ForceIntraFrame();
47- // encoder.SetMaxBitrate(2000000);
48- // encoder.SetTargetFps(16.9f);
45+ var data = BitmapToImageData (bmp );
46+ RgbImage rgb = new RgbImage (w , h );
4947
48+ for (int j = 0 ; j < 10 ; j ++ )
49+ {
50+
51+ encoder .Encode (data , out EncodedData [] ec )
52+
5053 foreach (var encoded in ec )
5154 {
55+ bool keyframe = encoded .FrameType == FrameType .I || encoded .FrameType == FrameType .IDR ;
5256 // encoded.GetBytes();
53- // encoded.CopyTo(buffer,offset,count );
57+ // encoded.CopyTo(buffer,offset);
5458
55- if (decoder .Decode (encoded , noDelay : true , out DecodingState ds , out RGBImage rgb ))
59+ if (decoder .Decode (encoded , noDelay : true , out DecodingState ds , ref rgb ))
5660 {
57- Bitmap result = RgbToBitmap (rgb );
58- // result.Save("Ok.bmp");
61+ Console .WriteLine ($" F:{encoded .FrameType } size: {encoded .Length }" );
62+ // Bitmap result = RgbToBitmap(rgb);
63+ // result.Save("Ok1.bmp");
5964 }
6065 }
6166 }
67+
68+ encoder .Dispose ();
69+ decoder .Dispose ();
70+ }
6271```
6372Bitmaps are not included on library to keep it cross platform.
6473<br />For the bitmaps and other image container types i will provide extention libraries.
6574``` c#
66- private static Bitmap RgbToBitmap (RGBImage img )
67- {
68- Bitmap bmp = new Bitmap (img .Width ,
69- img .Height ,
70- img .Width * 3 ,
71- PixelFormat .Format24bppRgb ,
72- img .ImageBytes );
73- return bmp ;
74- }
75+ private static Bitmap RgbToBitmap (RgbImage img )
76+ {
77+ Bitmap bmp = new Bitmap (img .Width ,
78+ img .Height ,
79+ img .Width * 3 ,
80+ PixelFormat .Format24bppRgb ,
81+ img .ImageBytes );
82+ return bmp ;
83+ }
7584```
7685And to extract bitmap data:
7786``` c#
@@ -81,43 +90,156 @@ And to extract bitmap data:
8190 * On a little-endian machine, like yours and many others,
8291 * the little end is stored first, so the byte order is b g r a.
8392 */
84- private static GenericImage BitmapToGenericImage (Bitmap bmp )
85- {
86- int width = bmp .Width ;
87- int height = bmp .Height ;
88- BitmapData bmpData = bmp .LockBits (new Rectangle (0 , 0 , width , height ),
89- ImageLockMode .ReadOnly ,
90- PixelFormat .Format32bppArgb );
91- var bmpScan = bmpData .Scan0 ;
92-
93- // PixelFormat.Format32bppArgb is default
94- var img = new GenericImage ();
95- switch (bmp .PixelFormat )
93+ private static ImageData BitmapToImageData (Bitmap bmp )
9694 {
97- case PixelFormat .Format32bppArgb :
98- img .ImgType = ImageType .Bgra ; // endianness
99- break ;
100- case PixelFormat .Format32bppRgb :
101- img .ImgType = ImageType .Bgra ;
102- break ;
103- case PixelFormat .Format24bppRgb :
104- img .ImgType = ImageType .Bgr ;
105- break ;
106- default :
107- throw new NotSupportedException ($" Format {bmp .PixelFormat } is not supported" );
95+ int width = bmp .Width ;
96+ int height = bmp .Height ;
97+ BitmapData bmpData = bmp .LockBits (new Rectangle (0 , 0 , width , height ),
98+ ImageLockMode .ReadOnly ,
99+ PixelFormat .Format32bppArgb );
100+ var bmpScan = bmpData .Scan0 ;
108101
109- }
102+ // PixelFormat.Format32bppArgb is default
103+ ImageType type = ImageType .Rgb ;
104+ switch (bmp .PixelFormat )
105+ {
106+ case PixelFormat .Format32bppArgb :
107+ type = ImageType .Bgra ; // endianness
108+ break ;
109+ case PixelFormat .Format32bppRgb :
110+ type = ImageType .Bgra ;
111+ break ;
112+ case PixelFormat .Format24bppRgb :
113+ type = ImageType .Bgr ;
114+ break ;
115+ default :
116+ throw new NotSupportedException ($" Format {bmp .PixelFormat } is not supported" );
110117
111- img .Width = width ;
112- img .Height = height ;
113- img .Stride = bmpData .Stride ;
114- img .ImageBytes = bmpScan ;
118+ }
119+
120+ var img = new H264Sharp .ImageData (type , width , height , bmpData .Stride , bmpScan );
115121
116- bmp .UnlockBits (bmpData );
117- return img ;
122+ bmp .UnlockBits (bmpData );
123+ return img ;
124+ }
118125}
119126```
120127
128+ # Advanced Configuration & Features
129+ ## Advanced Setup
130+ If you want to initialise your encoder and able to control everything, you can use provided API which is identical to Ciso C++.
131+ ``` c#
132+ encoder = new H264Encoder ();
133+ var param = encoder .GetDefaultParameters ();
134+
135+ param .iUsageType = EUsageType .CAMERA_VIDEO_REAL_TIME ;
136+ param .iPicWidth = w ;
137+ param .iPicHeight = h ;
138+ param .iTargetBitrate = 1000000 ;
139+ param .iTemporalLayerNum = 1 ;
140+ param .iSpatialLayerNum = 1 ;
141+ param .iRCMode = RC_MODES .RC_BITRATE_MODE ;
142+
143+ param .sSpatialLayers [0 ].iVideoWidth = 0 ;
144+ param .sSpatialLayers [0 ].iVideoWidth = 0 ;
145+ param .sSpatialLayers [0 ].fFrameRate = 60 ;
146+ param .sSpatialLayers [0 ].iSpatialBitrate = 1000000 ;
147+ param .sSpatialLayers [0 ].uiProfileIdc = EProfileIdc .PRO_HIGH ;
148+ param .sSpatialLayers [0 ].uiLevelIdc = 0 ;
149+ param .sSpatialLayers [0 ].iDLayerQp = 0 ;
150+
151+
152+ param .iComplexityMode = ECOMPLEXITY_MODE .HIGH_COMPLEXITY ;
153+ param .uiIntraPeriod = 300 ;
154+ param .iNumRefFrame = 0 ;
155+ param .eSpsPpsIdStrategy = EParameterSetStrategy .SPS_LISTING_AND_PPS_INCREASING ;
156+ param .bPrefixNalAddingCtrl = false ;
157+ param .bEnableSSEI = true ;
158+ param .bSimulcastAVC = false ;
159+ param .iPaddingFlag = 0 ;
160+ param .iEntropyCodingModeFlag = 1 ;
161+ param .bEnableFrameSkip = false ;
162+ param .iMaxBitrate = 0 ;
163+ param .iMinQp = 0 ;
164+ param .iMaxQp = 51 ;
165+ param .uiMaxNalSize = 0 ;
166+ param .bEnableLongTermReference = true ;
167+ param .iLTRRefNum = 1 ;
168+ param .iLtrMarkPeriod = 180 ;
169+ param .iMultipleThreadIdc = 1 ;
170+ param .bUseLoadBalancing = true ;
171+
172+ param .bEnableDenoise = false ;
173+ param .bEnableBackgroundDetection = true ;
174+ param .bEnableAdaptiveQuant = true ;
175+ param .bEnableSceneChangeDetect = true ;
176+ param .bIsLosslessLink = false ;
177+ param .bFixRCOverShoot = true ;
178+ param .iIdrBitrateRatio = 400 ;
179+ param .fMaxFrameRate = 30 ;
180+
181+ encoder .Initialize (param );
182+ ```
183+
184+ Similarly for decoder
185+ ``` c#
186+ decoder = new H264Decoder ();
187+ TagSVCDecodingParam decParam = new TagSVCDecodingParam ();
188+ decParam .uiTargetDqLayer = 0x ff ;
189+ decParam .eEcActiveIdc = ERROR_CON_IDC .ERROR_CON_SLICE_MV_COPY_CROSS_IDR ;
190+ decParam .eEcActiveIdc = ERROR_CON_IDC .ERROR_CON_FRAME_COPY_CROSS_IDR ;
191+ decParam .sVideoProperty .eVideoBsType = VIDEO_BITSTREAM_TYPE .VIDEO_BITSTREAM_SVC ;
192+ decoder .Initialize (decParam );
193+ ```
194+
195+ Image format conversion (RGB <-> YUV420) has optional configuration where you can provide number of threads on parallelisation.
196+ <br />Using 1 thread gives consumes least cpu cycles and most efficient but it takes more time.
197+ Beyond 4 threads you start to get diminishing returns.
198+ <br />Fastest performance is achieved when threadcount is same as your phyical threads on your machine.
199+ Larger the image more effective is the parallelisation.
200+ Default count is 4.
201+ ``` c#
202+ encoder .ConverterNumberOfThreads = Environment .ProcessorCount ;
203+ decoder .ConverterNumberOfThreads = 4 ;
204+ ```
205+
206+ You can configure on RGB to YUV conversion wether to use SSE or table based converter. SSE is faster and is default configuration.
207+
208+ ``` c#
209+ decoder .EnableSSEYUVConversion = true ;
210+ ```
211+
212+ ## Options
213+ You can get and set options to decoder and encoder on runtime. All options API is implemented 1-1 with cisco api.
214+
215+ ``` c#
216+ encoder .SetOption (ENCODER_OPTION .ENCODER_OPTION_IDR_INTERVAL , 600 );
217+ encoder .GetOption (ENCODER_OPTION .ENCODER_OPTION_IDR_INTERVAL , out int idrPeriod );
218+ decoder .GetOption (DECODER_OPTION .DECODER_OPTION_FRAME_NUM , out tempInt );
219+ .. .
220+ ```
221+
222+ There are many possible options and they are commented on the enum fields as well as required types. If you want more detail search as general H264 options ad params not cisco spesifically.
223+ <br />Because you wont find any documentation on cisco side.
224+
225+ If you want to reuse your option structs for efficiency, you can use this method:
226+ ``` c#
227+ SEncoderStatistics ss ;
228+ SDecoderStatistics ss1 ;
229+ encoder .GetOptionRef (ENCODER_OPTION .ENCODER_OPTION_GET_STATISTICS , ref ss );
230+ decoder .GetOptionRef (DECODER_OPTION .DECODER_OPTION_GET_STATISTICS , ref ss1 );
231+ ```
232+ # Example App
233+ A simple example WPF application is provided. here you can explore:
234+ - Advanced Setup and their effects.
235+ - Using LTR references and loss recovery.
236+ - Recording audio and video.
237+ <img src =" https://github.com/ReferenceType/H264Sharp/assets/109621184/e530be0b-30df-4937-b5e5-6a5e970c81ba " width =50% height =50% >
238+
239+
240+
241+
242+
121243
122244# Legacy C++/CLI(deprecated)
123245### C++Cli wrapper is deprecated due to platform limitations and other issues. Plase use native Pinvoke version which is also distrbuted with Nuget.
0 commit comments