Skip to content

Commit f96f3a9

Browse files
committed
2 parents cf06c28 + b304473 commit f96f3a9

File tree

1 file changed

+184
-62
lines changed

1 file changed

+184
-62
lines changed

README.md

Lines changed: 184 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
# H264Sharp
22
Cisco'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

1012
Library 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

1315
C# library is .Net standard wrapper library for this dll and performs PInvoke to handle transcoding.
1416
## Nuget
1517
Install 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

1921
For 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

2729
Following code shows encoder and decoder in action, commented lines are for hints.
2830
``` c#
31+
2932
static 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
```
6372
Bitmaps 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
```
7685
And 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 = 0xff;
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

Comments
 (0)