/*
* Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Handle = Interop.ThumbnailExtractorHandle;
using Native = Interop.ThumbnailExtractor;
namespace Tizen.Multimedia.Util
{
///
/// Provides the ability to extract the thumbnail from media files.
///
/// 4
public static class ThumbnailExtractor
{
private static Handle CreateHandle()
{
Native.Create(out var handle).ThrowIfError("Failed to extract.");
return handle;
}
///
/// Extracts the thumbnail for the given media with the specified path.
///
/// 4
/// A task that represents the asynchronous extracting operation.
/// The size of the thumbnail will be the default size (320x240).
/// The path of the media file to extract the thumbnail.
/// is null.
/// does not exist.
/// An internal error occurs.
/// The caller does not have required privilege for accessing the .
/// The specified file is not supported.
public static Task ExtractAsync(string path)
{
return RunExtractAsync(path, null, CancellationToken.None);
}
///
/// Extracts the thumbnail for the given media with the specified path.
///
/// A task that represents the asynchronous extracting operation.
/// The size of the thumbnail will be the default size(320x240).
/// The path of the media file to extract the thumbnail.
/// The token to stop the operation.
/// is null.
/// does not exist.
/// An internal error occurs.
/// The caller does not have required privilege for accessing the .
/// The specified file is not supported.
/// 4
public static Task ExtractAsync(string path, CancellationToken cancellationToken)
{
return RunExtractAsync(path, null, cancellationToken);
}
///
/// Extracts the thumbnail for the given media with the specified path and size.
///
/// 4
/// A task that represents the asynchronous extracting operation.
///
/// If the width is not a multiple of 8, it can be changed by the inner process.
/// The width will be a multiple of 8 greater than the set value.
///
/// The path of the media file to extract the thumbnail.
/// The size of the thumbnail.
/// is null.
/// does not exist.
/// An internal error occurs.
/// The caller does not have required privilege for accessing the .
///
/// The width or the height of is less than or equal to zero.
///
/// The specified file is not supported.
public static Task ExtractAsync(string path, Size size)
{
return RunExtractAsync(path, size, CancellationToken.None);
}
///
/// Extracts the thumbnail for the given media with the specified path and size.
///
/// 4
/// A task that represents the asynchronous extracting operation.
///
/// If the width is not a multiple of 8, it can be changed by the inner process.
/// The width will be a multiple of 8 greater than the set value.
///
/// The path of the media file to extract the thumbnail.
/// The size of the thumbnail.
/// The token to stop the operation.
/// is null.
/// does not exist.
/// An internal error occurs.
/// The caller does not have required privilege for accessing the .
///
/// The width or the height of is less than or equal to zero.
///
/// The specified file is not supported.
public static Task ExtractAsync(string path, Size size,
CancellationToken cancellationToken)
{
return RunExtractAsync(path, size, cancellationToken);
}
private static Task RunExtractAsync(string path, Size? size,
CancellationToken cancellationToken)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
if (File.Exists(path) == false)
{
throw new FileNotFoundException("File does not exists.", path);
}
if (size.HasValue)
{
if (size.Value.Width <= 0)
{
throw new ArgumentOutOfRangeException(nameof(size), size.Value.Width,
"The width must be greater than zero.");
}
if (size.Value.Height <= 0)
{
throw new ArgumentOutOfRangeException(nameof(size), size.Value.Height,
"The height must be greater than zero.");
}
}
return cancellationToken.IsCancellationRequested ?
Task.FromCanceled(cancellationToken) :
ExtractAsyncCore(path, size, cancellationToken);
}
private static async Task ExtractAsyncCore(string path, Size? size,
CancellationToken cancellationToken)
{
using (var handle = CreateHandle())
{
Native.SetPath(handle, path).ThrowIfError("Failed to extract; failed to set the path.");
if (size.HasValue)
{
Native.SetSize(handle, size.Value.Width, size.Value.Height).
ThrowIfError("Failed to extract; failed to set the size");
}
var tcs = new TaskCompletionSource();
IntPtr id = IntPtr.Zero;
try
{
var cb = GetCallback(tcs);
using (var cbKeeper = ObjectKeeper.Get(cb))
{
Native.Extract(handle, cb, IntPtr.Zero, out id)
.ThrowIfError("Failed to extract.");
using (RegisterCancellationToken(tcs, cancellationToken, handle, Marshal.PtrToStringAnsi(id)))
{
return await tcs.Task;
}
}
}
finally
{
LibcSupport.Free(id);
}
}
}
private static Native.ThumbnailExtractCallback GetCallback(TaskCompletionSource tcs)
{
return (error, requestId, thumbWidth, thumbHeight, thumbData, dataSize, _) =>
{
if (error == ThumbnailExtractorError.None)
{
try
{
byte[] tmpBuf = new byte[dataSize];
Marshal.Copy(thumbData, tmpBuf, 0, dataSize);
tcs.TrySetResult(new ThumbnailExtractionResult(tmpBuf, thumbWidth, thumbHeight));
}
catch (Exception e)
{
tcs.TrySetException(new InvalidOperationException("[" + error + "] Failed to copy data.", e));
}
finally
{
LibcSupport.Free(thumbData);
}
}
else
{
tcs.TrySetException(error.ToException("Failed to extract."));
}
};
}
private static IDisposable RegisterCancellationToken(TaskCompletionSource tcs,
CancellationToken cancellationToken, Handle handle, string id)
{
if (cancellationToken.CanBeCanceled == false)
{
return null;
}
return cancellationToken.Register(() =>
{
if (tcs.Task.IsCompleted)
{
return;
}
Native.Cancel(handle, id).ThrowIfError("Failed to cancel.");
tcs.TrySetCanceled();
});
}
}
}