Release 4.0.0-preview1-00201
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.Util / ThumbnailExtractor / ThumbnailExtractor.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.IO;
19 using System.Runtime.InteropServices;
20 using System.Threading;
21 using System.Threading.Tasks;
22 using Handle = Interop.ThumbnailExtractorHandle;
23 using Native = Interop.ThumbnailExtractor;
24
25 namespace Tizen.Multimedia.Util
26 {
27     /// <summary>
28     /// Provides the ability to extract the thumbnail from media files.
29     /// </summary>
30     public static class ThumbnailExtractor
31     {
32         private static Handle CreateHandle()
33         {
34             Native.Create(out var handle).ThrowIfError("Failed to extract.");
35
36             return handle;
37         }
38
39         /// <summary>
40         /// Extracts the thumbnail for the given media with the specified path.
41         /// </summary>
42         /// <since_tizen> 3 </since_tizen>
43         /// <returns>A task that represents the asynchronous extracting operation.</returns>
44         /// <remarks>The size of the thumbnail will be the default size (320x240).</remarks>
45         /// <param name="path">The path of the media file to extract the thumbnail.</param>
46         /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
47         /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
48         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
49         /// <exception cref="UnauthorizedAccessException">The caller does not have required privilege for accessing the <paramref name="path"/>.</exception>
50         /// <exception cref="FileFormatException">The specified file is not supported.</exception>
51         public static Task<ThumbnailExtractionResult> ExtractAsync(string path)
52         {
53             return RunExtractAsync(path, null, CancellationToken.None);
54         }
55
56         /// <summary>
57         /// Extracts the thumbnail for the given media with the specified path.
58         /// </summary>
59         /// <returns>A task that represents the asynchronous extracting operation.</returns>
60         /// <remarks>The size of the thumbnail will be the default size(320x240).\n</remarks>
61         /// <param name="path">The path of the media file to extract the thumbnail.</param>
62         /// <param name="cancellationToken">The token to stop the operation.</param>
63         /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
64         /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
65         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
66         /// <exception cref="UnauthorizedAccessException">The caller does not have required privilege for accessing the <paramref name="path"/>.</exception>
67         /// <exception cref="FileFormatException">The specified file is not supported.</exception>
68         public static Task<ThumbnailExtractionResult> ExtractAsync(string path, CancellationToken cancellationToken)
69         {
70             return RunExtractAsync(path, null, cancellationToken);
71         }
72
73         /// <summary>
74         /// Extracts the thumbnail for the given media with the specified path and size.
75         /// </summary>
76         /// <since_tizen> 3 </since_tizen>
77         /// <returns>A task that represents the asynchronous extracting operation.</returns>
78         /// <remarks>
79         /// If the width is not a multiple of 8, it can be changed by the inner process.\n
80         /// The width will be a multiple of 8 greater than the set value.
81         /// </remarks>
82         /// <param name="path">The path of the media file to extract the thumbnail.</param>
83         /// <param name="size">The size of the thumbnail.</param>
84         /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
85         /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
86         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
87         /// <exception cref="UnauthorizedAccessException">The caller does not have required privilege for accessing the <paramref name="path"/>.</exception>
88         /// <exception cref="ArgumentOutOfRangeException">
89         ///     The width or the height of <paramref name="size"/> is less than or equal to zero.
90         /// </exception>
91         /// <exception cref="FileFormatException">The specified file is not supported.</exception>
92         public static Task<ThumbnailExtractionResult> ExtractAsync(string path, Size size)
93         {
94             return RunExtractAsync(path, size, CancellationToken.None);
95         }
96
97         /// <summary>
98         /// Extracts the thumbnail for the given media with the specified path and size.
99         /// </summary>
100         /// <since_tizen> 3 </since_tizen>
101         /// <returns>A task that represents the asynchronous extracting operation.</returns>
102         /// <remarks>
103         /// If the width is not a multiple of 8, it can be changed by the inner process.\n
104         /// The width will be a multiple of 8 greater than the set value.
105         /// </remarks>
106         /// <param name="path">The path of the media file to extract the thumbnail.</param>
107         /// <param name="size">The size of the thumbnail.</param>
108         /// <param name="cancellationToken">The token to stop the operation.</param>
109         /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
110         /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
111         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
112         /// <exception cref="UnauthorizedAccessException">The caller does not have required privilege for accessing the <paramref name="path"/>.</exception>
113         /// <exception cref="ArgumentOutOfRangeException">
114         ///     The width or the height of <paramref name="size"/> is less than or equal to zero.
115         /// </exception>
116         /// <exception cref="FileFormatException">The specified file is not supported.</exception>
117         public static Task<ThumbnailExtractionResult> ExtractAsync(string path, Size size,
118             CancellationToken cancellationToken)
119         {
120             return RunExtractAsync(path, size, cancellationToken);
121         }
122
123         private static Task<ThumbnailExtractionResult> RunExtractAsync(string path, Size? size,
124             CancellationToken cancellationToken)
125         {
126             if (path == null)
127             {
128                 throw new ArgumentNullException(nameof(path));
129             }
130
131             if (File.Exists(path) == false)
132             {
133                 throw new FileNotFoundException("File does not exists.", path);
134             }
135
136             if (size.HasValue)
137             {
138                 if (size.Value.Width <= 0)
139                 {
140                     throw new ArgumentOutOfRangeException(nameof(size), size.Value.Width,
141                         "The width must be greater than zero.");
142                 }
143
144                 if (size.Value.Height <= 0)
145                 {
146                     throw new ArgumentOutOfRangeException(nameof(size), size.Value.Height,
147                         "The height must be greater than zero.");
148                 }
149             }
150
151             return cancellationToken.IsCancellationRequested ?
152                 Task.FromCanceled<ThumbnailExtractionResult>(cancellationToken) :
153                 ExtractAsyncCore(path, size, cancellationToken);
154         }
155
156
157         private static async Task<ThumbnailExtractionResult> ExtractAsyncCore(string path, Size? size,
158             CancellationToken cancellationToken)
159         {
160             using (var handle = CreateHandle())
161             {
162                 Native.SetPath(handle, path).ThrowIfError("Failed to extract; failed to set the path.");
163
164                 if (size.HasValue)
165                 {
166                     Native.SetSize(handle, size.Value.Width, size.Value.Height).
167                         ThrowIfError("Failed to extract; failed to set the size");
168                 }
169
170                 var tcs = new TaskCompletionSource<ThumbnailExtractionResult>();
171
172                 IntPtr id = IntPtr.Zero;
173
174                 try
175                 {
176                     Native.Extract(handle, GetCallback(tcs), IntPtr.Zero, out id)
177                         .ThrowIfError("Failed to extract.");
178
179                     using (RegisterCancellationToken(tcs, cancellationToken, handle, Marshal.PtrToStringAnsi(id)))
180                     {
181                         return await tcs.Task;
182                     }
183                 }
184                 finally
185                 {
186                     LibcSupport.Free(id);
187                 }
188             }
189         }
190
191         private static Native.ThumbnailExtractCallback GetCallback(TaskCompletionSource<ThumbnailExtractionResult> tcs)
192         {
193             return (error, requestId, thumbWidth, thumbHeight, thumbData, dataSize, _) =>
194             {
195                 if (error == ThumbnailExtractorError.None)
196                 {
197                     try
198                     {
199                         byte[] tmpBuf = new byte[dataSize];
200                         Marshal.Copy(thumbData, tmpBuf, 0, dataSize);
201
202                         tcs.SetResult(new ThumbnailExtractionResult(tmpBuf, thumbWidth, thumbHeight));
203                     }
204                     catch (Exception e)
205                     {
206                         tcs.SetException(new InvalidOperationException("[" + error + "] Failed to copy data.", e));
207                     }
208                     finally
209                     {
210                         LibcSupport.Free(thumbData);
211                     }
212                 }
213                 else
214                 {
215                     tcs.SetException(error.ToException("Failed to extract."));
216                 }
217             };
218         }
219
220         private static IDisposable RegisterCancellationToken(TaskCompletionSource<ThumbnailExtractionResult> tcs,
221             CancellationToken cancellationToken, Handle handle, string id)
222         {
223             if (cancellationToken.CanBeCanceled == false)
224             {
225                 return null;
226             }
227
228             return cancellationToken.Register(() =>
229             {
230                 if (tcs.Task.IsCompleted)
231                 {
232                     return;
233                 }
234
235                 Native.Cancel(handle, id).ThrowIfError("Failed to cancel.");
236                 tcs.TrySetCanceled();
237             });
238         }
239     }
240 }