2 * Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 using System.Runtime.InteropServices;
20 using System.Threading;
21 using System.Threading.Tasks;
22 using Handle = Interop.ThumbnailExtractorHandle;
23 using Native = Interop.ThumbnailExtractor;
25 namespace Tizen.Multimedia.Util
28 /// Provides the ability to extract the thumbnail from media files.
30 /// <since_tizen> 4 </since_tizen>
31 public static class ThumbnailExtractor
33 private static Handle CreateHandle()
35 Native.Create(out var handle).ThrowIfError("Failed to extract.");
41 /// Extracts the thumbnail for the given media with the specified path.
43 /// <since_tizen> 4 </since_tizen>
44 /// <returns>A task that represents the asynchronous extracting operation.</returns>
45 /// <remarks>The size of the thumbnail will be the default size (320x240).</remarks>
46 /// <param name="path">The path of the media file to extract the thumbnail.</param>
47 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
48 /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
49 /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
50 /// <exception cref="UnauthorizedAccessException">The caller does not have required privilege for accessing the <paramref name="path"/>.</exception>
51 /// <exception cref="FileFormatException">The specified file is not supported.</exception>
52 public static Task<ThumbnailExtractionResult> ExtractAsync(string path)
54 return RunExtractAsync(path, null, CancellationToken.None);
58 /// Extracts the thumbnail for the given media with the specified path.
60 /// <returns>A task that represents the asynchronous extracting operation.</returns>
61 /// <remarks>The size of the thumbnail will be the default size(320x240).</remarks>
62 /// <param name="path">The path of the media file to extract the thumbnail.</param>
63 /// <param name="cancellationToken">The token to stop the operation.</param>
64 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
65 /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
66 /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
67 /// <exception cref="UnauthorizedAccessException">The caller does not have required privilege for accessing the <paramref name="path"/>.</exception>
68 /// <exception cref="FileFormatException">The specified file is not supported.</exception>
69 /// <since_tizen> 4 </since_tizen>
70 public static Task<ThumbnailExtractionResult> ExtractAsync(string path, CancellationToken cancellationToken)
72 return RunExtractAsync(path, null, cancellationToken);
76 /// Extracts the thumbnail for the given media with the specified path and size.
78 /// <since_tizen> 4 </since_tizen>
79 /// <returns>A task that represents the asynchronous extracting operation.</returns>
81 /// If the width is not a multiple of 8, it can be changed by the inner process.<br/>
82 /// The width will be a multiple of 8 greater than the set value.
84 /// <param name="path">The path of the media file to extract the thumbnail.</param>
85 /// <param name="size">The size of the thumbnail.</param>
86 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
87 /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
88 /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
89 /// <exception cref="UnauthorizedAccessException">The caller does not have required privilege for accessing the <paramref name="path"/>.</exception>
90 /// <exception cref="ArgumentOutOfRangeException">
91 /// The width or the height of <paramref name="size"/> is less than or equal to zero.
93 /// <exception cref="FileFormatException">The specified file is not supported.</exception>
94 public static Task<ThumbnailExtractionResult> ExtractAsync(string path, Size size)
96 return RunExtractAsync(path, size, CancellationToken.None);
100 /// Extracts the thumbnail for the given media with the specified path and size.
102 /// <since_tizen> 4 </since_tizen>
103 /// <returns>A task that represents the asynchronous extracting operation.</returns>
105 /// If the width is not a multiple of 8, it can be changed by the inner process.<br/>
106 /// The width will be a multiple of 8 greater than the set value.
108 /// <param name="path">The path of the media file to extract the thumbnail.</param>
109 /// <param name="size">The size of the thumbnail.</param>
110 /// <param name="cancellationToken">The token to stop the operation.</param>
111 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
112 /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
113 /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
114 /// <exception cref="UnauthorizedAccessException">The caller does not have required privilege for accessing the <paramref name="path"/>.</exception>
115 /// <exception cref="ArgumentOutOfRangeException">
116 /// The width or the height of <paramref name="size"/> is less than or equal to zero.
118 /// <exception cref="FileFormatException">The specified file is not supported.</exception>
119 public static Task<ThumbnailExtractionResult> ExtractAsync(string path, Size size,
120 CancellationToken cancellationToken)
122 return RunExtractAsync(path, size, cancellationToken);
125 private static Task<ThumbnailExtractionResult> RunExtractAsync(string path, Size? size,
126 CancellationToken cancellationToken)
130 throw new ArgumentNullException(nameof(path));
133 if (File.Exists(path) == false)
135 throw new FileNotFoundException("File does not exists.", path);
140 if (size.Value.Width <= 0)
142 throw new ArgumentOutOfRangeException(nameof(size), size.Value.Width,
143 "The width must be greater than zero.");
146 if (size.Value.Height <= 0)
148 throw new ArgumentOutOfRangeException(nameof(size), size.Value.Height,
149 "The height must be greater than zero.");
153 return cancellationToken.IsCancellationRequested ?
154 Task.FromCanceled<ThumbnailExtractionResult>(cancellationToken) :
155 ExtractAsyncCore(path, size, cancellationToken);
159 private static async Task<ThumbnailExtractionResult> ExtractAsyncCore(string path, Size? size,
160 CancellationToken cancellationToken)
162 using (var handle = CreateHandle())
164 Native.SetPath(handle, path).ThrowIfError("Failed to extract; failed to set the path.");
168 Native.SetSize(handle, size.Value.Width, size.Value.Height).
169 ThrowIfError("Failed to extract; failed to set the size");
172 var tcs = new TaskCompletionSource<ThumbnailExtractionResult>();
174 IntPtr id = IntPtr.Zero;
178 var cb = GetCallback(tcs);
179 using (var cbKeeper = ObjectKeeper.Get(cb))
181 Native.Extract(handle, cb, IntPtr.Zero, out id)
182 .ThrowIfError("Failed to extract.");
184 using (RegisterCancellationToken(tcs, cancellationToken, handle, Marshal.PtrToStringAnsi(id)))
186 return await tcs.Task;
192 LibcSupport.Free(id);
197 private static Native.ThumbnailExtractCallback GetCallback(TaskCompletionSource<ThumbnailExtractionResult> tcs)
199 return (error, requestId, thumbWidth, thumbHeight, thumbData, dataSize, _) =>
201 if (error == ThumbnailExtractorError.None)
205 tcs.TrySetResult(new ThumbnailExtractionResult(thumbData, thumbWidth, thumbHeight, dataSize));
209 tcs.TrySetException(new InvalidOperationException("[" + error + "] Failed to create ThumbnailExtractionResult instance.", e));
213 LibcSupport.Free(thumbData);
218 tcs.TrySetException(error.ToException("Failed to extract."));
223 private static IDisposable RegisterCancellationToken(TaskCompletionSource<ThumbnailExtractionResult> tcs,
224 CancellationToken cancellationToken, Handle handle, string id)
226 if (cancellationToken.CanBeCanceled == false)
231 return cancellationToken.Register(() =>
233 if (tcs.Task.IsCompleted)
238 Native.Cancel(handle, id).ThrowIfError("Failed to cancel.");
239 tcs.TrySetCanceled();
244 /// Extracts the thumbnail for the given media with the specified path and size.
245 /// The generated thumbnail will be returned in <see cref="ThumbnailExtractionResult"/>.
248 /// The size of generated thumbnail will be 320x240.<br/>
249 /// But, if the size of <paramref name="path"/> has different ratio with 320x240 (approximately 1.33:1),<br/>
250 /// 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/>
251 /// 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/>
252 /// If you want to set the size, which is different with 320x240, please use <see cref="Extract(string, Size)"/>.<br/>
254 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage. <br/>
255 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
257 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
258 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
259 /// <param name="path">The path of the media file to extract the thumbnail.</param>
260 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
261 /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
262 /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
263 /// <exception cref="FileFormatException">The specified file is not supported.</exception>
264 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
265 /// <returns>The result of extracting operation.</returns>
266 /// <since_tizen> 6 </since_tizen>
267 public static ThumbnailExtractionResult Extract(string path)
269 return Extract(path, new Size(320, 240));
273 /// Extracts the thumbnail for the given media with the specified path and size.
274 /// The generated thumbnail will be returned in <see cref="ThumbnailExtractionResult"/>.
277 /// The size of generated thumbnail will be <paramref name="size"/>.<br/>
278 /// But, if the size of <paramref name="path"/> has different ratio with <paramref name="size"/>,<br/>
279 /// 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/>
280 /// For example, if the size of <paramref name="path"/> is 900x500 (1.8:1)) and <paramref name="size"/> is 320x240,<br/>
281 /// the size of generated thumbnail is 432x240(1.8:1).<br/>
283 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage. <br/>
284 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
286 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
287 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
288 /// <param name="path">The path of the media file to extract the thumbnail.</param>
289 /// <param name="size">The size of the thumbnail.</param>
290 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
291 /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
292 /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
293 /// <exception cref="ArgumentOutOfRangeException">
294 /// The width or the height of <paramref name="size"/> is less than or equal to zero.
296 /// <exception cref="FileFormatException">The specified file is not supported.</exception>
297 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
298 /// <returns>The result of extracting operation.</returns>
299 /// <since_tizen> 6 </since_tizen>
300 public static ThumbnailExtractionResult Extract(string path, Size size)
304 throw new ArgumentNullException(nameof(path));
307 if (File.Exists(path) == false)
309 throw new FileNotFoundException("File does not exists.", path);
314 throw new ArgumentOutOfRangeException(nameof(size), size.Width,
315 "The width must be greater than zero.");
318 if (size.Height <= 0)
320 throw new ArgumentOutOfRangeException(nameof(size), size.Height,
321 "The height must be greater than zero.");
324 Native.ExtractToBuffer(path, (uint)size.Width, (uint)size.Height, out IntPtr thumbData,
325 out int dataSize, out uint thumbWidth, out uint thumbHeight).
326 ThrowIfError("Failed to extract thumbnail to buffer");
330 return new ThumbnailExtractionResult(thumbData, (int)thumbWidth, (int)thumbHeight,
335 if (thumbData != IntPtr.Zero)
337 LibcSupport.Free(thumbData);
343 /// Extracts the thumbnail for the given media with the specified path and size.
344 /// The generated thumbnail will be saved in <paramref name="resultThumbnailPath"/>.
347 /// The size of <paramref name="resultThumbnailPath"/> image will be 320x240.<br/>
348 /// But, if the size of <paramref name="path"/> has different ratio with 320x240 (approximately 1.33:1),<br/>
349 /// 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/>
350 /// 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/>
351 /// If you want to set the size, which is different with 320x240, please use <see cref="Extract(string, Size, string)"/>.<br/>
353 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage. <br/>
354 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
356 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
357 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
358 /// <param name="path">The path of the media file to extract the thumbnail.</param>
359 /// <param name="resultThumbnailPath">The path to save the generated thumbnail.</param>
360 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
361 /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
362 /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
363 /// <exception cref="FileFormatException">The specified file is not supported.</exception>
364 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
365 /// <since_tizen> 6 </since_tizen>
366 public static void Extract(string path, string resultThumbnailPath)
368 Extract(path, new Size(320, 240), resultThumbnailPath);
372 /// Extracts the thumbnail for the given media with the specified path and size.
373 /// The generated thumbnail will be saved in <paramref name="resultThumbnailPath"/>.
376 /// The size of <paramref name="resultThumbnailPath"/> image will be <paramref name="size"/>.<br/>
377 /// But, if the size of <paramref name="path"/> has different ratio with <paramref name="size"/>,<br/>
378 /// 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/>
379 /// For example, if the size of <paramref name="path"/> is 900x500 (1.8:1) and <paramref name="size"/> is 320x240,<br/>
380 /// the size of <paramref name="resultThumbnailPath"/> is 432x240(1.8:1).<br/>
382 /// If you want to access internal storage, you should add privilege http://tizen.org/privilege/mediastorage. <br/>
383 /// If you want to access external storage, you should add privilege http://tizen.org/privilege/externalstorage.
385 /// <privilege>http://tizen.org/privilege/mediastorage</privilege>
386 /// <privilege>http://tizen.org/privilege/externalstorage</privilege>
387 /// <param name="path">The path of the media file to extract the thumbnail.</param>
388 /// <param name="size">The size of the thumbnail.</param>
389 /// <param name="resultThumbnailPath">The path to save the generated thumbnail.</param>
390 /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
391 /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
392 /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
393 /// <exception cref="ArgumentOutOfRangeException">
394 /// The width or the height of <paramref name="size"/> is less than or equal to zero.
396 /// <exception cref="FileFormatException">The specified file is not supported.</exception>
397 /// <exception cref="UnauthorizedAccessException">The caller has no required privilege.</exception>
398 /// <since_tizen> 6 </since_tizen>
399 public static void Extract(string path, Size size, string resultThumbnailPath)
403 throw new ArgumentNullException(nameof(path));
406 if (File.Exists(path) == false)
408 throw new FileNotFoundException("File does not exists.", path);
413 throw new ArgumentOutOfRangeException(nameof(size), size.Width,
414 "The width must be greater than zero.");
417 if (size.Height <= 0)
419 throw new ArgumentOutOfRangeException(nameof(size), size.Height,
420 "The height must be greater than zero.");
423 Native.ExtractToFile(path, (uint)size.Width, (uint)size.Height, resultThumbnailPath).
424 ThrowIfError("Failed to extract thumbnail to file.");