[Camera] Support variable buffer size (#3934)
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.Camera / Camera / PreviewFrame.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 using System;
18 using System.Diagnostics;
19 using System.Runtime.InteropServices;
20 using static Interop.Camera;
21
22 namespace Tizen.Multimedia
23 {
24     /// <summary>
25     /// The class containing the preview image data.
26     /// </summary>
27     /// <since_tizen> 4 </since_tizen>
28     public class PreviewFrame
29     {
30         private const uint _variableBufferMargin = 2;
31
32         internal PreviewFrame(IntPtr ptr, ref PinnedPreviewBuffer<byte> buffers)
33         {
34             var unmanagedStruct = Marshal.PtrToStructure<CameraPreviewDataStruct>(ptr);
35
36             Format = unmanagedStruct.Format;
37             Resolution = new Size(unmanagedStruct.Width, unmanagedStruct.Height);
38             TimeStamp = unmanagedStruct.TimeStamp;
39             PlaneType = GetPlaneType(unmanagedStruct);
40             CreatePlane(unmanagedStruct, ref buffers);
41         }
42
43         private static PlaneType GetPlaneType(CameraPreviewDataStruct unmanagedStruct)
44         {
45             if (unmanagedStruct.NumOfPlanes == 1)
46             {
47                 switch (unmanagedStruct.Format)
48                 {
49                     case CameraPixelFormat.H264:
50                     case CameraPixelFormat.Jpeg:
51                     case CameraPixelFormat.Mjpeg:
52                     case CameraPixelFormat.Vp8:
53                     case CameraPixelFormat.Vp9:
54                         return PlaneType.EncodedPlane;
55                     case CameraPixelFormat.Invz:
56                         return PlaneType.DepthPlane;
57                     case CameraPixelFormat.Rgba:
58                     case CameraPixelFormat.Argb:
59                         return PlaneType.RgbPlane;
60                     default:
61                         return PlaneType.SinglePlane;
62                 }
63             }
64             else if (unmanagedStruct.NumOfPlanes == 2)
65             {
66                 return PlaneType.DoublePlane;
67             }
68
69             return PlaneType.TriplePlane;
70         }
71
72         internal void CreatePlane(CameraPreviewDataStruct unmanagedStruct, ref PinnedPreviewBuffer<byte> buffers)
73         {
74             switch (PlaneType)
75             {
76                 case PlaneType.SinglePlane:
77                     var singlePlane = unmanagedStruct.Plane.SinglePlane;
78
79                     if (buffers == null || CheckReallocation(unmanagedStruct, ref buffers))
80                     {
81                         buffers = new PinnedPreviewBuffer<byte>(singlePlane.DataLength);
82                     }
83
84                     Marshal.Copy(singlePlane.Data, buffers[0], 0, (int)singlePlane.DataLength);
85                     Plane = new SinglePlane(buffers[0]);
86
87                     break;
88                 case PlaneType.DoublePlane:
89                     var doublePlane = unmanagedStruct.Plane.DoublePlane;
90
91                     doublePlane.YLength = (uint)(Resolution.Width * Resolution.Height);
92                     doublePlane.UVLength = (uint)(Resolution.Width * Resolution.Height) / 2;
93
94                     if (buffers == null || CheckReallocation(unmanagedStruct, ref buffers))
95                     {
96                         buffers = new PinnedPreviewBuffer<byte>(doublePlane.YLength, doublePlane.UVLength);
97                     }
98
99                     Marshal.Copy(doublePlane.Y, buffers[0], 0, (int)doublePlane.YLength);
100                     Marshal.Copy(doublePlane.UV, buffers[1], 0, (int)doublePlane.UVLength);
101                     Plane =  new DoublePlane(buffers[0], buffers[1]);
102
103                     break;
104                 case PlaneType.TriplePlane:
105                     var triplePlane = unmanagedStruct.Plane.TriplePlane;
106
107                     if (buffers == null || CheckReallocation(unmanagedStruct, ref buffers))
108                     {
109                         buffers = new PinnedPreviewBuffer<byte>(triplePlane.YLength, triplePlane.ULength, triplePlane.VLength);
110                     }
111
112                     Marshal.Copy(triplePlane.Y, buffers[0], 0, (int)triplePlane.YLength);
113                     Marshal.Copy(triplePlane.U, buffers[1], 0, (int)triplePlane.ULength);
114                     Marshal.Copy(triplePlane.V, buffers[2], 0, (int)triplePlane.VLength);
115                     Plane =  new TriplePlane(buffers[0], buffers[1], buffers[2]);
116
117                     break;
118                 case PlaneType.EncodedPlane:
119                     var encodedPlane = unmanagedStruct.Plane.EncodedPlane;
120
121                     if (buffers == null || CheckReallocation(unmanagedStruct, ref buffers))
122                     {
123                         // We take buffer margin to avoid reallocation as much as possible.
124                         buffers = new PinnedPreviewBuffer<byte>(encodedPlane.DataLength * _variableBufferMargin);
125                     }
126
127                     Marshal.Copy(encodedPlane.Data, buffers[0], 0, (int)encodedPlane.DataLength);
128                     Plane = new EncodedPlane(buffers[0], encodedPlane.IsDeltaFrame);
129
130                     break;
131                 case PlaneType.DepthPlane:
132                     var depthPlane = unmanagedStruct.Plane.DepthPlane;
133
134                     if (buffers == null || CheckReallocation(unmanagedStruct, ref buffers))
135                     {
136                         buffers = new PinnedPreviewBuffer<byte>(depthPlane.DataLength);
137                     }
138
139                     Marshal.Copy(depthPlane.Data, buffers[0], 0, (int)depthPlane.DataLength);
140                     Plane = new DepthPlane(buffers[0]);
141
142                     break;
143                 case PlaneType.RgbPlane:
144                     var rgbPlane = unmanagedStruct.Plane.RgbPlane;
145
146                     if (buffers == null || CheckReallocation(unmanagedStruct, ref buffers))
147                     {
148                         buffers = new PinnedPreviewBuffer<byte>(rgbPlane.DataLength);
149                     }
150                     Marshal.Copy(rgbPlane.Data, buffers[0], 0, (int)rgbPlane.DataLength);
151
152                     Plane = new RgbPlane(buffers[0]);
153                     break;
154                 default:
155                     Debug.Fail("Unknown preview data!");
156                     break;
157             }
158         }
159
160         internal static uint GetMaxPreviewPlaneSize(IntPtr ptr)
161         {
162             uint size = 0;
163             var unmanagedStruct = Marshal.PtrToStructure<CameraPreviewDataStruct>(ptr);
164
165             switch (GetPlaneType(unmanagedStruct))
166             {
167                 case PlaneType.SinglePlane:
168                     size = unmanagedStruct.Plane.SinglePlane.DataLength;
169                     break;
170                 case PlaneType.DoublePlane:
171                     size = unmanagedStruct.Plane.DoublePlane.UVLength;
172                     break;
173                 case PlaneType.TriplePlane:
174                     size = unmanagedStruct.Plane.TriplePlane.YLength;
175                     break;
176                 case PlaneType.EncodedPlane:
177                     size = unmanagedStruct.Plane.EncodedPlane.DataLength;
178                     break;
179                 case PlaneType.DepthPlane:
180                     size = unmanagedStruct.Plane.DepthPlane.DataLength;
181                     break;
182                 case PlaneType.RgbPlane:
183                     size = unmanagedStruct.Plane.RgbPlane.DataLength;
184                     break;
185                 default:
186                     Debug.Fail("Unknown preview data!");
187                     break;
188             }
189
190             return size;
191         }
192
193         internal bool CheckReallocation(CameraPreviewDataStruct unmanagedStruct, ref PinnedPreviewBuffer<byte> buffers)
194         {
195             bool isReallocation = false;
196
197             switch (PlaneType)
198             {
199                 case PlaneType.SinglePlane:
200                     var singlePlane = unmanagedStruct.Plane.SinglePlane;
201
202                     if (buffers[0].Length < singlePlane.DataLength)
203                     {
204                         isReallocation = true;
205                     }
206
207                     break;
208                 case PlaneType.DoublePlane:
209                     var doublePlane = unmanagedStruct.Plane.DoublePlane;
210
211                     doublePlane.YLength = (uint)(Resolution.Width * Resolution.Height);
212                     doublePlane.UVLength = (uint)(Resolution.Width * Resolution.Height) / 2;
213
214                     if (buffers[0].Length < doublePlane.YLength || buffers[1].Length < doublePlane.UVLength)
215                     {
216                         isReallocation = true;
217                     }
218
219                     break;
220                 case PlaneType.TriplePlane:
221                     var triplePlane = unmanagedStruct.Plane.TriplePlane;
222
223                     if (buffers[0].Length < triplePlane.YLength || buffers[1].Length < triplePlane.ULength || buffers[2].Length < triplePlane.VLength)
224                     {
225                         isReallocation = true;
226                     }
227
228                     break;
229                 case PlaneType.EncodedPlane:
230                     var encodedPlane = unmanagedStruct.Plane.EncodedPlane;
231
232                     if (buffers[0].Length < encodedPlane.DataLength)
233                     {
234                         Log.Debug(CameraLog.Tag, $"Cur size:{buffers[0].Length} -> New size:{encodedPlane.DataLength * _variableBufferMargin}");
235                         isReallocation = true;
236                     }
237
238                     break;
239                 case PlaneType.DepthPlane:
240                     var depthPlane = unmanagedStruct.Plane.DepthPlane;
241
242                     if (buffers[0].Length < depthPlane.DataLength)
243                     {
244                         isReallocation = true;
245                     }
246
247                     break;
248                 case PlaneType.RgbPlane:
249                     var rgbPlane = unmanagedStruct.Plane.RgbPlane;
250
251                     if (buffers[0].Length < rgbPlane.DataLength)
252                     {
253                         isReallocation = true;
254                     }
255
256                     break;
257                 default:
258                     Debug.Fail("Unknown preview data!");
259                     break;
260             }
261
262             if (isReallocation)
263             {
264                 Log.Debug(CameraLog.Tag, "Reallocate preview buffer.");
265
266                 // Dispose current buffer to free GCHandle.
267                 buffers.Dispose();
268             }
269
270             return isReallocation;
271         }
272
273         /// <summary>
274         /// The pixel format of the image.
275         /// </summary>
276         /// <since_tizen> 4 </since_tizen>
277         public CameraPixelFormat Format { get; }
278
279         /// <summary>
280         /// The resolution of the preview image.
281         /// </summary>
282         /// <since_tizen> 4 </since_tizen>
283         public Size Resolution { get; }
284
285         /// <summary>
286         /// The time stamp of the preview frame.
287         /// </summary>
288         /// <since_tizen> 4 </since_tizen>
289         public uint TimeStamp { get; }
290
291         /// <summary>
292         /// The type of the preview plane. <see cref="Tizen.Multimedia.PlaneType"/>
293         /// </summary>
294         /// <since_tizen> 4 </since_tizen>
295         public PlaneType PlaneType { get; }
296
297         /// <summary>
298         /// The buffer including the preview frame.
299         /// </summary>
300         /// <since_tizen> 4 </since_tizen>
301         public IPreviewPlane Plane { get; private set;}
302     }
303 }