Release 4.0.0-preview1-00201
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia.Metadata / MetadataExtractor / MetadataExtractor.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
21 namespace Tizen.Multimedia
22 {
23     static internal class MetadataExtractorLog
24     {
25         internal const string Tag = "Tizen.Multimedia.MetadataExtractor";
26     }
27
28     /// <summary>
29     /// Provides a means to get the metadata from a media file.
30     /// </summary>
31     public class MetadataExtractor : IDisposable
32     {
33         private bool _disposed = false;
34         private IntPtr _handle = IntPtr.Zero;
35         private IntPtr _buffer = IntPtr.Zero;
36
37         private void Create(Func<MetadataExtractorError> initFunc)
38         {
39             MetadataExtractorRetValidator.ThrowIfError(
40                 Interop.MetadataExtractor.Create(out _handle), "Failed to create metadata");
41
42             try
43             {
44                 MetadataExtractorRetValidator.ThrowIfError(initFunc(), "Failed to init");
45
46                 _metadata = new Lazy<Metadata>(() => new Metadata(Handle));
47             }
48             catch
49             {
50                 Release();
51                 throw;
52             }
53         }
54
55         /// <summary>
56         /// Initializes a new instance of the MetadataExtractor class with the specified path.
57         /// </summary>
58         /// <since_tizen> 3 </since_tizen>
59         /// <param name="path">The path for the file to extract the metadata.</param>
60         /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
61         /// <exception cref="FileNotFoundException"><paramref name="path"/> does not exist.</exception>
62         public MetadataExtractor(string path)
63         {
64             if (path == null)
65             {
66                 throw new ArgumentNullException(nameof(path));
67             }
68
69             Create(() => Interop.MetadataExtractor.SetPath(_handle, path));
70         }
71
72         /// <summary>
73         /// Initializes a new instance of the MetadataExtractor class with the specified buffer.
74         /// </summary>
75         /// <since_tizen> 3 </since_tizen>
76         /// <param name="buffer">The buffer to extract the metadata.</param>
77         /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
78         /// <exception cref="ArgumentException">The length of <paramref name="buffer"/> is zero.</exception>
79         public MetadataExtractor(byte[] buffer)
80         {
81             if (buffer == null)
82             {
83                 throw new ArgumentNullException(nameof(buffer));
84             }
85
86             if (buffer.Length == 0)
87             {
88                 throw new ArgumentException("buffer length is zero.", nameof(buffer));
89             }
90
91             _buffer = Marshal.AllocHGlobal(buffer.Length);
92             Marshal.Copy(buffer, 0, _buffer, buffer.Length);
93
94             try
95             {
96                 Create(() => Interop.MetadataExtractor.SetBuffer(_handle, _buffer, buffer.Length));
97             }
98             catch (Exception)
99             {
100                 Marshal.FreeHGlobal(_buffer);
101                 throw;
102             }
103         }
104
105         private IntPtr Handle
106         {
107             get
108             {
109                 if (_disposed)
110                 {
111                     throw new ObjectDisposedException(nameof(MetadataExtractor));
112                 }
113
114                 return _handle;
115             }
116         }
117
118         private Lazy<Metadata> _metadata;
119
120         /// <summary>
121         /// Retrieves the <see cref="Metadata"/>.
122         /// </summary>
123         /// <since_tizen> 3 </since_tizen>
124         /// <returns>The <see cref="Metadata"/> for the given source.</returns>
125         /// <exception cref="InvalidOperationException">An internal process error occurs.</exception>
126         /// <exception cref="ObjectDisposedException">The <see cref="MetadataExtractor"/> has been already disposed of.</exception>
127         public Metadata GetMetadata()
128         {
129             if (_disposed)
130             {
131                 throw new ObjectDisposedException(nameof(MetadataExtractor));
132             }
133
134             return _metadata.Value;
135         }
136
137         /// <summary>
138         /// Gets the artwork image in the source.
139         /// </summary>
140         /// <since_tizen> 3 </since_tizen>
141         /// <returns>The <see cref="Artwork"/> if it exists, otherwise null.</returns>
142         /// <exception cref="InvalidOperationException">An internal process error occurs.</exception>
143         /// <exception cref="ObjectDisposedException">The <see cref="MetadataExtractor"/> has been already disposed of.</exception>
144         public Artwork GetArtwork()
145         {
146             IntPtr data = IntPtr.Zero;
147             IntPtr mimeType = IntPtr.Zero;
148
149             try
150             {
151                 int size = 0;
152
153                 var ret = Interop.MetadataExtractor.GetArtwork(Handle, out data, out size, out mimeType);
154                 MetadataExtractorRetValidator.ThrowIfError(ret, "Failed to get value");
155
156                 if (size > 0)
157                 {
158                     var buf = new byte[size];
159                     Marshal.Copy(data, buf, 0, size);
160
161                     return new Artwork(buf, Marshal.PtrToStringAnsi(mimeType));
162                 }
163
164                 return null;
165             }
166             finally
167             {
168                 Interop.Libc.Free(data);
169                 Interop.Libc.Free(mimeType);
170             }
171         }
172
173         /// <summary>
174         /// Gets the sync lyrics of the source.
175         /// </summary>
176         /// <since_tizen> 3 </since_tizen>
177         /// <param name="index">The index of lyrics to retrieve.</param>
178         /// <returns>The <see cref="SyncLyrics"/> object if <paramref name="index"/> is valid, otherwise null.</returns>
179         /// <exception cref="InvalidOperationException">An internal process error occurs.</exception>
180         /// <exception cref="ObjectDisposedException">The <see cref="MetadataExtractor"/> has been already disposed of.</exception>
181         public SyncLyrics GetSyncLyrics(int index)
182         {
183             IntPtr lyrics = IntPtr.Zero;
184
185             try
186             {
187                 uint timestamp = 0;
188
189                 var ret = Interop.MetadataExtractor.GetSynclyrics(Handle, index, out timestamp, out lyrics);
190                 MetadataExtractorRetValidator.ThrowIfError(ret, "Failed to get sync lyrics");
191
192                 if (lyrics == IntPtr.Zero)
193                 {
194                     return null;
195                 }
196
197                 return new SyncLyrics(Marshal.PtrToStringAnsi(lyrics), timestamp);
198             }
199             finally
200             {
201                 Interop.Libc.Free(lyrics);
202             }
203         }
204
205         /// <summary>
206         /// Gets the frame of a video media.
207         /// </summary>
208         /// <since_tizen> 3 </since_tizen>
209         /// <returns>The raw thumbnail data in RGB888 if it exists, otherwise null.</returns>
210         /// <exception cref="InvalidOperationException">An internal process error occurs.</exception>
211         /// <exception cref="ObjectDisposedException">The <see cref="MetadataExtractor"/> has been already disposed of.</exception>
212         public byte[] GetVideoThumbnail()
213         {
214             IntPtr data = IntPtr.Zero;
215
216             try
217             {
218                 int size = 0;
219
220                 var ret = Interop.MetadataExtractor.GetFrame(Handle, out data, out size);
221                 MetadataExtractorRetValidator.ThrowIfError(ret, "Failed to get value");
222
223                 if (size == 0)
224                 {
225                     return null;
226                 }
227
228                 var buf = new byte[size];
229                 Marshal.Copy(data, buf, 0, size);
230
231                 return buf;
232             }
233             finally
234             {
235                 Interop.Libc.Free(data);
236             }
237         }
238
239         /// <summary>
240         /// Gets the frame of a video media.
241         /// </summary>
242         /// <since_tizen> 3 </since_tizen>
243         /// <param name="timeStamp">The timestamp in milliseconds.</param>
244         /// <param name="accurate">true to get an accurate frame for the given timestamp,
245         ///     otherwise false to get the nearest i-frame of the video rapidly.</param>
246         /// <returns>The raw frame data in RGB888 if a frame at specified time exists, otherwise null.</returns>
247         /// <exception cref="InvalidOperationException">An internal error occurs.</exception>
248         /// <exception cref="ObjectDisposedException">The <see cref="MetadataExtractor"/> has been already disposed of.</exception>
249         public byte[] GetFrameAt(uint timeStamp, bool accurate)
250         {
251             IntPtr data = IntPtr.Zero;
252
253             try
254             {
255                 int size = 0;
256
257                 var ret = Interop.MetadataExtractor.GetFrameAtTime(Handle, timeStamp, accurate, out data, out size);
258                 MetadataExtractorRetValidator.ThrowIfError(ret, "Failed to get value");
259
260                 if (size == 0)
261                 {
262                     return null;
263                 }
264
265                 var buf = new byte[size];
266                 Marshal.Copy(data, buf, 0, size);
267
268                 return buf;
269             }
270             finally
271             {
272                 Interop.Libc.Free(data);
273             }
274         }
275
276         ~MetadataExtractor()
277         {
278             Dispose(false);
279         }
280
281         protected virtual void Dispose(bool disposing)
282         {
283             if (!_disposed)
284             {
285                 Release();
286                 _disposed = true;
287             }
288         }
289
290         private void Release()
291         {
292             if (_buffer != IntPtr.Zero)
293             {
294                 Marshal.FreeHGlobal(_buffer);
295             }
296
297             if (_handle != IntPtr.Zero)
298             {
299                 var ret = Interop.MetadataExtractor.Destroy(_handle);
300                 Log.Error(MetadataExtractorLog.Tag, $"DestroyHandle failed : {ret}.");
301
302                 _handle = IntPtr.Zero;
303             }
304         }
305
306         /// <summary>
307         /// Releases all resources used by the <see cref="MetadataExtractor"/> object.
308         /// </summary>
309         public void Dispose()
310         {
311             Dispose(true);
312             GC.SuppressFinalize(this);
313         }
314     }
315 }