f1e09ff2487c6e78cdd3632fd07eca033f794b97
[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     /// <since_tizen> 4 </since_tizen>
31     public static class ThumbnailExtractor
32     {
33         private static Handle CreateHandle()
34         {
35             Native.Create(out var handle).ThrowIfError("Failed to extract.");
36
37             return handle;
38         }
39
40         /// <summary>
41         /// Extracts the thumbnail for the given media with the specified path.
42         /// </summary>
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)
53         {
54             return RunExtractAsync(path, null, CancellationToken.None);
55         }
56
57         /// <summary>
58         /// Extracts the thumbnail for the given media with the specified path.
59         /// </summary>
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)
71         {
72             return RunExtractAsync(path, null, cancellationToken);
73         }
74
75         /// <summary>
76         /// Extracts the thumbnail for the given media with the specified path and size.
77         /// </summary>
78         /// <since_tizen> 4 </since_tizen>
79         /// <returns>A task that represents the asynchronous extracting operation.</returns>
80         /// <remarks>
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.
83         /// </remarks>
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.
92         /// </exception>
93         /// <exception cref="FileFormatException">The specified file is not supported.</exception>
94         public static Task<ThumbnailExtractionResult> ExtractAsync(string path, Size size)
95         {
96             return RunExtractAsync(path, size, CancellationToken.None);
97         }
98
99         /// <summary>
100         /// Extracts the thumbnail for the given media with the specified path and size.
101         /// </summary>
102         /// <since_tizen> 4 </since_tizen>
103         /// <returns>A task that represents the asynchronous extracting operation.</returns>
104         /// <remarks>
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.
107         /// </remarks>
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.
117         /// </exception>
118         /// <exception cref="FileFormatException">The specified file is not supported.</exception>
119         public static Task<ThumbnailExtractionResult> ExtractAsync(string path, Size size,
120             CancellationToken cancellationToken)
121         {
122             return RunExtractAsync(path, size, cancellationToken);
123         }
124
125         private static Task<ThumbnailExtractionResult> RunExtractAsync(string path, Size? size,
126             CancellationToken cancellationToken)
127         {
128             if (path == null)
129             {
130                 throw new ArgumentNullException(nameof(path));
131             }
132
133             if (File.Exists(path) == false)
134             {
135                 throw new FileNotFoundException("File does not exists.", path);
136             }
137
138             if (size.HasValue)
139             {
140                 if (size.Value.Width <= 0)
141                 {
142                     throw new ArgumentOutOfRangeException(nameof(size), size.Value.Width,
143                         "The width must be greater than zero.");
144                 }
145
146                 if (size.Value.Height <= 0)
147                 {
148                     throw new ArgumentOutOfRangeException(nameof(size), size.Value.Height,
149                         "The height must be greater than zero.");
150                 }
151             }
152
153             return cancellationToken.IsCancellationRequested ?
154                 Task.FromCanceled<ThumbnailExtractionResult>(cancellationToken) :
155                 ExtractAsyncCore(path, size, cancellationToken);
156         }
157
158
159         private static async Task<ThumbnailExtractionResult> ExtractAsyncCore(string path, Size? size,
160             CancellationToken cancellationToken)
161         {
162             using (var handle = CreateHandle())
163             {
164                 Native.SetPath(handle, path).ThrowIfError("Failed to extract; failed to set the path.");
165
166                 if (size.HasValue)
167                 {
168                     Native.SetSize(handle, size.Value.Width, size.Value.Height).
169                         ThrowIfError("Failed to extract; failed to set the size");
170                 }
171
172                 var tcs = new TaskCompletionSource<ThumbnailExtractionResult>();
173
174                 IntPtr id = IntPtr.Zero;
175
176                 try
177                 {
178                     var cb = GetCallback(tcs);
179                     using (var cbKeeper = ObjectKeeper.Get(cb))
180                     {
181                         Native.Extract(handle, cb, IntPtr.Zero, out id)
182                             .ThrowIfError("Failed to extract.");
183
184                         using (RegisterCancellationToken(tcs, cancellationToken, handle, Marshal.PtrToStringAnsi(id)))
185                         {
186                             return await tcs.Task;
187                         }
188                     }
189                 }
190                 finally
191                 {
192                     LibcSupport.Free(id);
193                 }
194             }
195         }
196
197         private static Native.ThumbnailExtractCallback GetCallback(TaskCompletionSource<ThumbnailExtractionResult> tcs)
198         {
199             return (error, requestId, thumbWidth, thumbHeight, thumbData, dataSize, _) =>
200             {
201                 if (error == ThumbnailExtractorError.None)
202                 {
203                     try
204                     {
205                         byte[] tmpBuf = new byte[dataSize];
206                         Marshal.Copy(thumbData, tmpBuf, 0, dataSize);
207
208                         tcs.TrySetResult(new ThumbnailExtractionResult(tmpBuf, thumbWidth, thumbHeight));
209                     }
210                     catch (Exception e)
211                     {
212                         tcs.TrySetException(new InvalidOperationException("[" + error + "] Failed to copy data.", e));
213                     }
214                     finally
215                     {
216                         LibcSupport.Free(thumbData);
217                     }
218                 }
219                 else
220                 {
221                     tcs.TrySetException(error.ToException("Failed to extract."));
222                 }
223             };
224         }
225
226         private static IDisposable RegisterCancellationToken(TaskCompletionSource<ThumbnailExtractionResult> tcs,
227             CancellationToken cancellationToken, Handle handle, string id)
228         {
229             if (cancellationToken.CanBeCanceled == false)
230             {
231                 return null;
232             }
233
234             return cancellationToken.Register(() =>
235             {
236                 if (tcs.Task.IsCompleted)
237                 {
238                     return;
239                 }
240
241                 Native.Cancel(handle, id).ThrowIfError("Failed to cancel.");
242                 tcs.TrySetCanceled();
243             });
244         }
245     }
246 }