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