2cf3bea4c0940a1c17c179d111c6c5427f1c1127
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.Util / ThumbnailExtractor / ThumbnailExtractor.cs
1 /*
2  * Copyright (c) 2018 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.IO;
19 using System.Threading;
20 using System.Threading.Tasks;
21 using Native = Interop.ThumbnailExtractor;
22
23 namespace Tizen.Multimedia.Util
24 {
25     /// <summary>
26     /// Provides the ability to extract the thumbnail from media files.
27     /// </summary>
28     /// <since_tizen> 4 </since_tizen>
29     public static class ThumbnailExtractor
30     {
31         /// <summary>
32         /// Extracts the thumbnail for the given media with the specified path.
33         /// </summary>
34         /// <since_tizen> 4 </since_tizen>
35         /// <returns>A task that represents the asynchronous extracting operation.</returns>
36         /// <remarks>The size of the thumbnail will be the default size (320x240).</remarks>
37         /// <param name="path">The path of the media file to extract the thumbnail.</param>
38         /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
39         /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
40         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
41         /// <exception cref="UnauthorizedAccessException">The caller does not have required privilege for accessing the <paramref name="path"/>.</exception>
42         /// <exception cref="FileFormatException">The specified file is not supported.</exception>
43         public static Task<ThumbnailExtractionResult> ExtractAsync(string path)
44         {
45             return RunExtractAsync(path, null, CancellationToken.None);
46         }
47
48         /// <summary>
49         /// Extracts the thumbnail for the given media with the specified path.
50         /// </summary>
51         /// <returns>A task that represents the asynchronous extracting operation.</returns>
52         /// <remarks>The size of the thumbnail will be the default size(320x240).</remarks>
53         /// <param name="path">The path of the media file to extract the thumbnail.</param>
54         /// <param name="cancellationToken">The token to stop the operation.</param>
55         /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
56         /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
57         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
58         /// <exception cref="UnauthorizedAccessException">The caller does not have required privilege for accessing the <paramref name="path"/>.</exception>
59         /// <exception cref="FileFormatException">The specified file is not supported.</exception>
60         /// <since_tizen> 4 </since_tizen>
61         public static Task<ThumbnailExtractionResult> ExtractAsync(string path, CancellationToken cancellationToken)
62         {
63             return RunExtractAsync(path, null, cancellationToken);
64         }
65
66         /// <summary>
67         /// Extracts the thumbnail for the given media with the specified path and size.
68         /// </summary>
69         /// <since_tizen> 4 </since_tizen>
70         /// <returns>A task that represents the asynchronous extracting operation.</returns>
71         /// <remarks>
72         /// If the width is not a multiple of 8, it can be changed by the inner process.<br/>
73         /// The width will be a multiple of 8 greater than the set value.
74         /// </remarks>
75         /// <param name="path">The path of the media file to extract the thumbnail.</param>
76         /// <param name="size">The size of the thumbnail.</param>
77         /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
78         /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
79         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
80         /// <exception cref="UnauthorizedAccessException">The caller does not have required privilege for accessing the <paramref name="path"/>.</exception>
81         /// <exception cref="ArgumentOutOfRangeException">
82         ///     The width or the height of <paramref name="size"/> is less than or equal to zero.
83         /// </exception>
84         /// <exception cref="FileFormatException">The specified file is not supported.</exception>
85         public static Task<ThumbnailExtractionResult> ExtractAsync(string path, Size size)
86         {
87             return RunExtractAsync(path, size, CancellationToken.None);
88         }
89
90         /// <summary>
91         /// Extracts the thumbnail for the given media with the specified path and size.
92         /// </summary>
93         /// <since_tizen> 4 </since_tizen>
94         /// <returns>A task that represents the asynchronous extracting operation.</returns>
95         /// <remarks>
96         /// If the width is not a multiple of 8, it can be changed by the inner process.<br/>
97         /// The width will be a multiple of 8 greater than the set value.
98         /// </remarks>
99         /// <param name="path">The path of the media file to extract the thumbnail.</param>
100         /// <param name="size">The size of the thumbnail.</param>
101         /// <param name="cancellationToken">The token to stop the operation.</param>
102         /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
103         /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
104         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
105         /// <exception cref="UnauthorizedAccessException">The caller does not have required privilege for accessing the <paramref name="path"/>.</exception>
106         /// <exception cref="ArgumentOutOfRangeException">
107         ///     The width or the height of <paramref name="size"/> is less than or equal to zero.
108         /// </exception>
109         /// <exception cref="FileFormatException">The specified file is not supported.</exception>
110         public static Task<ThumbnailExtractionResult> ExtractAsync(string path, Size size,
111             CancellationToken cancellationToken)
112         {
113             return RunExtractAsync(path, size, cancellationToken);
114         }
115
116         private static Task<ThumbnailExtractionResult> RunExtractAsync(string path, Size? size,
117             CancellationToken cancellationToken)
118         {
119             if (path == null)
120             {
121                 throw new ArgumentNullException(nameof(path));
122             }
123
124             if (!File.Exists(path))
125             {
126                 throw new FileNotFoundException("File does not exists.", path);
127             }
128
129             if (size.HasValue)
130             {
131                 if (size.Value.Width <= 0)
132                 {
133                     throw new ArgumentOutOfRangeException(nameof(size), size.Value.Width,
134                         "The width must be greater than zero.");
135                 }
136
137                 if (size.Value.Height <= 0)
138                 {
139                     throw new ArgumentOutOfRangeException(nameof(size), size.Value.Height,
140                         "The height must be greater than zero.");
141                 }
142             }
143
144             return cancellationToken.IsCancellationRequested ?
145                 Task.FromCanceled<ThumbnailExtractionResult>(cancellationToken) :
146                 ExtractAsyncCore(path, size, cancellationToken);
147         }
148
149         private static async Task<ThumbnailExtractionResult> ExtractAsyncCore(string path, Size? size,
150             CancellationToken cancellationToken)
151         {
152             var tcs = new TaskCompletionSource<ThumbnailExtractionResult>();
153
154             Task thumbTask = null;
155
156             if (cancellationToken.CanBeCanceled)
157             {
158                 cancellationToken.Register(() =>
159                 {
160                     if (tcs.Task.IsCompleted)
161                     {
162                         return;
163                     }
164
165                     tcs.TrySetCanceled();
166                 });
167             }
168
169             thumbTask = Task.Factory.StartNew( () =>
170             {
171                 try
172                 {
173                     var result = Extract(path, size.HasValue ? size.Value : new Size(320, 240));
174                     if (result != null)
175                     {
176                         tcs.TrySetResult(result);
177                     }
178                     else
179                     {
180                         tcs.TrySetException(new InvalidOperationException("Failed to extract thumbnail"));
181                     }
182                 }
183                 catch (Exception e)
184                 {
185                     Log.Error("Tizen.Multimedia.Util", e.ToString());
186                     tcs.TrySetException(e);
187                 }
188             }, cancellationToken,
189                 TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning,
190                 TaskScheduler.Default);
191
192             return await tcs.Task;
193         }
194
195         /// <summary>
196         /// Extracts the thumbnail for the given media with the specified path and size.
197         /// The generated thumbnail will be returned in <see cref="ThumbnailExtractionResult"/>.
198         /// </summary>
199         /// <remarks>
200         /// The size of generated thumbnail will be 320x240.<br/>
201         /// If the size of <paramref name="path"/> has different ratio from 320x240 (approximately 1.33:1),<br/>
202         /// thumbnail is generated in a way to keep the ratio of <paramref name="path"/>, which is based on short axis of <paramref name="path"/>.<br/>
203         /// For example, if the size of <paramref name="path"/> is 900x500 (1.8:1), the size of generated thumbnail is 432x240(1.8:1).<br/>
204         /// To set the size different from 320x240, please use <see cref="Extract(string, Size)"/>.<br/>
205         /// <br/>
206         /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage. <br/>
207         /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
208         /// </remarks>
209         /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
210         /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
211         /// <param name="path">The path of the media file to extract the thumbnail.</param>
212         /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
213         /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
214         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
215         /// <exception cref="FileFormatException">The specified file is not supported.</exception>
216         /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
217         /// <returns>The result of extracting operation.</returns>
218         /// <since_tizen> 6 </since_tizen>
219         public static ThumbnailExtractionResult Extract(string path)
220         {
221             return Extract(path, new Size(320, 240));
222         }
223
224         /// <summary>
225         /// Extracts the thumbnail for the given media with the specified path and size.
226         /// The generated thumbnail will be returned in <see cref="ThumbnailExtractionResult"/>.
227         /// </summary>
228         /// <remarks>
229         /// The size of generated thumbnail will be <paramref name="size"/>.<br/>
230         /// But, if the size of <paramref name="path"/> has different ratio with <paramref name="size"/>,<br/>
231         /// thumbnail will be generated in a way to keep the ratio of <paramref name="path"/>, which based on short axis of <paramref name="path"/>.<br/>
232         /// For example, if the size of <paramref name="path"/> is 900x500 (1.8:1)) and <paramref name="size"/> is 320x240,<br/>
233         /// the size of generated thumbnail is 432x240(1.8:1).<br/>
234         /// <br/>
235         /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage. <br/>
236         /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
237         /// </remarks>
238         /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
239         /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
240         /// <param name="path">The path of the media file to extract the thumbnail.</param>
241         /// <param name="size">The size of the thumbnail.</param>
242         /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
243         /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
244         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
245         /// <exception cref="ArgumentOutOfRangeException">
246         ///     The width or the height of <paramref name="size"/> is less than or equal to zero.
247         /// </exception>
248         /// <exception cref="FileFormatException">The specified file is not supported.</exception>
249         /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
250         /// <returns>The result of extracting operation.</returns>
251         /// <since_tizen> 6 </since_tizen>
252         public static ThumbnailExtractionResult Extract(string path, Size size)
253         {
254             if (path == null)
255             {
256                 throw new ArgumentNullException(nameof(path));
257             }
258
259             if (File.Exists(path) == false)
260             {
261                 throw new FileNotFoundException("File does not exists.", path);
262             }
263
264             if (size.Width <= 0)
265             {
266                 throw new ArgumentOutOfRangeException(nameof(size), size.Width,
267                     "The width must be greater than zero.");
268             }
269
270             if (size.Height <= 0)
271             {
272                 throw new ArgumentOutOfRangeException(nameof(size), size.Height,
273                     "The height must be greater than zero.");
274             }
275
276             Native.ExtractToBuffer(path, (uint)size.Width, (uint)size.Height, out IntPtr thumbData,
277                 out int dataSize, out uint thumbWidth, out uint thumbHeight).
278                 ThrowIfError("Failed to extract thumbnail to buffer");
279
280             try
281             {
282                 return new ThumbnailExtractionResult(thumbData, (int)thumbWidth, (int)thumbHeight,
283                     dataSize);
284             }
285             finally
286             {
287                 if (thumbData != IntPtr.Zero)
288                 {
289                     LibcSupport.Free(thumbData);
290                 }
291             }
292         }
293
294         /// <summary>
295         /// Extracts the thumbnail for the given media with the specified path and size.
296         /// The generated thumbnail will be saved in <paramref name="resultThumbnailPath"/>.
297         /// </summary>
298         /// <remarks>
299         /// The size of <paramref name="resultThumbnailPath"/> image will be 320x240.<br/>
300         /// If the size of <paramref name="path"/> has different ratio with 320x240 (approximately 1.33:1),<br/>
301         /// thumbnail is generated in a way to keep the ratio of <paramref name="path"/>, which is based on short axis of <paramref name="path"/>.<br/>
302         /// For example, if the size of <paramref name="path"/> is 900x500 (1.8:1), the size of <paramref name="resultThumbnailPath"/> is 432x240(1.8:1).<br/>
303         /// To set the size different from 320x240, please use <see cref="Extract(string, Size, string)"/>.<br/>
304         /// <br/>
305         /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage. <br/>
306         /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
307         /// </remarks>
308         /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
309         /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
310         /// <param name="path">The path of the media file to extract the thumbnail.</param>
311         /// <param name="resultThumbnailPath">The path to save the generated thumbnail.</param>
312         /// <exception cref="ArgumentException"><paramref name="path"/> or <paramref name="resultThumbnailPath"/> is invalid.</exception>
313         /// <exception cref="ArgumentNullException"><paramref name="path"/> or <paramref name="resultThumbnailPath"/> is null.</exception>
314         /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
315         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
316         /// <exception cref="FileFormatException">The specified file is not supported.</exception>
317         /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
318         /// <since_tizen> 6 </since_tizen>
319         public static void Extract(string path, string resultThumbnailPath)
320         {
321             Extract(path, new Size(320, 240), resultThumbnailPath);
322         }
323
324         /// <summary>
325         /// Extracts the thumbnail for the given media with the specified path and size.
326         /// The generated thumbnail will be saved in <paramref name="resultThumbnailPath"/>.
327         /// </summary>
328         /// <remarks>
329         /// The size of <paramref name="resultThumbnailPath"/> image will be <paramref name="size"/>.<br/>
330         /// But, if the size of <paramref name="path"/> has different ratio with <paramref name="size"/>,<br/>
331         /// thumbnail will be generated in a way to keep the ratio of <paramref name="path"/>, which based on short axis of <paramref name="path"/>.<br/>
332         /// For example, if the size of <paramref name="path"/> is 900x500 (1.8:1) and <paramref name="size"/> is 320x240,<br/>
333         /// the size of <paramref name="resultThumbnailPath"/> is 432x240(1.8:1).<br/>
334         /// <br/>
335         /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage. <br/>
336         /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
337         /// </remarks>
338         /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
339         /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
340         /// <param name="path">The path of the media file to extract the thumbnail.</param>
341         /// <param name="size">The size of the thumbnail.</param>
342         /// <param name="resultThumbnailPath">The path to save the generated thumbnail.</param>
343         /// <exception cref="ArgumentException"><paramref name="path"/> or <paramref name="resultThumbnailPath"/> is invalid.</exception>
344         /// <exception cref="ArgumentNullException"><paramref name="path"/> or <paramref name="resultThumbnailPath"/> is null.</exception>
345         /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
346         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
347         /// <exception cref="ArgumentOutOfRangeException">
348         ///     The width or the height of <paramref name="size"/> is less than or equal to zero.
349         /// </exception>
350         /// <exception cref="FileFormatException">The specified file is not supported.</exception>
351         /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
352         /// <since_tizen> 6 </since_tizen>
353         public static void Extract(string path, Size size, string resultThumbnailPath)
354         {
355             ValidationUtil.ValidateIsNullOrEmpty(path, nameof(path));
356             ValidationUtil.ValidateIsNullOrEmpty(resultThumbnailPath, nameof(resultThumbnailPath));
357
358             if (File.Exists(path) == false)
359             {
360                 throw new FileNotFoundException("File does not exists.", path);
361             }
362
363             if (size.Width <= 0)
364             {
365                 throw new ArgumentOutOfRangeException(nameof(size), size.Width,
366                     "The width must be greater than zero.");
367             }
368
369             if (size.Height <= 0)
370             {
371                 throw new ArgumentOutOfRangeException(nameof(size), size.Height,
372                     "The height must be greater than zero.");
373             }
374
375             Native.ExtractToFile(path, (uint)size.Width, (uint)size.Height, resultThumbnailPath).
376                 ThrowIfError("Failed to extract thumbnail to file.");
377         }
378     }
379 }