/// <summary>
        /// Uploads the stream asynchronously
        /// </summary>
        /// <param name="stream">The stream to upload</param>
        /// <param name="blobTransferClient">The <see cref="BlobTransferClient"/> which is used to upload files.</param>
        /// <param name="locator">An locator <see cref="ILocator"/> which defines permissions associated with the Asset.</param>
        /// <param name="token">A <see cref="CancellationToken"/> to use for canceling upload operation.</param>
        /// <returns>A function delegate that returns the future result to be available through the Task.</returns>
        public Task UploadAsync(Stream stream, BlobTransferClient blobTransferClient, ILocator locator, CancellationToken token)
        {
            if (this.IsFragmented())
            {
                throw new NotSupportedException(StringTable.NotSupportedFragblobUpload);
            }

            if (blobTransferClient == null)
            {
                throw new ArgumentNullException("blobTransferClient");
            }

            if (locator == null)
            {
                throw new ArgumentNullException("locator");
            }

            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            IContentKey          contentKeyData       = null;
            FileEncryption       fileEncryption       = null;
            AssetCreationOptions assetCreationOptions = this.Asset.Options;

            if (assetCreationOptions.HasFlag(AssetCreationOptions.StorageEncrypted))
            {
                contentKeyData = this.Asset.ContentKeys.FirstOrDefault(c => c.ContentKeyType == ContentKeyType.StorageEncryption);
                if (contentKeyData == null)
                {
                    throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, StringTable.StorageEncryptionContentKeyIsMissing, this.Asset.Id));
                }

                fileEncryption = new FileEncryption(contentKeyData.GetClearKeyValue(), EncryptionUtils.GetKeyIdAsGuid(contentKeyData.Id));
                ulong iv = Convert.ToUInt64(this.InitializationVector, CultureInfo.InvariantCulture);
                fileEncryption.SetInitializationVectorForFile(this.Name, iv);
            }

            EventHandler <BlobTransferProgressChangedEventArgs> handler = (s, e) => OnUploadProgressChanged(this.Name, e);

            blobTransferClient.TransferProgressChanged += handler;

            MediaRetryPolicy retryPolicy = this.GetMediaContext().MediaServicesClassFactory.GetBlobStorageClientRetryPolicy();

            var streamLength = stream.Length;

            return(blobTransferClient.UploadBlob(
                       new Uri(locator.BaseUri),
                       this.Name,
                       stream,
                       null,
                       fileEncryption,
                       token,
                       retryPolicy.AsAzureStorageClientRetryPolicy(),
                       () => locator.ContentAccessComponent)
                   .ContinueWith(
                       ts =>
            {
                blobTransferClient.TransferProgressChanged -= handler;
                this.PostUploadAction(ts, streamLength, token);
            }));
        }
        private void AssetEncryptAction(string outputPath, bool overwriteExistingEncryptedFiles, CancellationToken cancellationToken, ConcurrentDictionary <string, IContentKey> keys, IIngestManifestAsset asset)
        {
            cancellationToken.ThrowIfCancellationRequested();

            List <Task> encryptTasks = new List <Task>();

            AssetCreationOptions assetCreationOptions = asset.Asset.Options;

            if (assetCreationOptions.HasFlag(AssetCreationOptions.StorageEncrypted))
            {
                IContentKey contentKeyData = keys[asset.Id];
                var         fileEncryption = new FileEncryption(contentKeyData.GetClearKeyValue(), EncryptionUtils.GetKeyIdAsGuid(contentKeyData.Id));


                foreach (IngestManifestFileData file in asset.IngestManifestFiles)
                {
                    ulong iv = Convert.ToUInt64(file.InitializationVector, CultureInfo.InvariantCulture);
                    fileEncryption.SetInitializationVectorForFile(file.Name, iv);

                    FileInfo fileInfo = null;
                    fileInfo = TrackedFilesPaths.ContainsKey(file.Id) ? new FileInfo(TrackedFilesPaths[file.Id]) : new FileInfo(file.Name);

                    string destinationPath = Path.Combine(outputPath, fileInfo.Name);
                    if (File.Exists(destinationPath))
                    {
                        if (overwriteExistingEncryptedFiles)
                        {
                            File.Delete(destinationPath);
                        }
                        else
                        {
                            throw new IOException(string.Format(CultureInfo.InvariantCulture, StringTable.BulkIngestFileExists, destinationPath));
                        }
                    }

                    long fileSize     = fileInfo.Length;
                    int  maxBlockSize = GetBlockSize(fileSize);

                    int numThreads = MaxNumberOfEncryptionThreadsForFilePerCore * Environment.ProcessorCount;
                    ConcurrentQueue <Tuple <int, int> > queue = PrepareUploadQueue(maxBlockSize, fileSize);
                    if (queue.Count < numThreads)
                    {
                        numThreads = queue.Count;
                    }


                    File.Create(destinationPath).Dispose();

                    Action action = GetEncryptionAction(cancellationToken, fileEncryption, file, destinationPath, fileInfo, queue, maxBlockSize);
                    for (int i = 0; i < numThreads; i++)
                    {
                        encryptTasks.Add(Task.Factory.StartNew((action), cancellationToken));
                    }
                }
                try
                {
                    Task.WaitAll(encryptTasks.ToArray());
                }
                finally
                {
                    fileEncryption.Dispose();
                }
            }
        }
        private void AssetEncryptAction(string outputPath, bool overwriteExistingEncryptedFiles, CancellationToken cancellationToken, ConcurrentDictionary<string, IContentKey> keys, IIngestManifestAsset asset)
        {
            cancellationToken.ThrowIfCancellationRequested();

            List<Task> encryptTasks = new List<Task>();

            AssetCreationOptions assetCreationOptions = asset.Asset.Options;
            if (assetCreationOptions.HasFlag(AssetCreationOptions.StorageEncrypted))
            {
                IContentKey contentKeyData = keys[asset.Id];
                var fileEncryption = new FileEncryption(contentKeyData.GetClearKeyValue(), EncryptionUtils.GetKeyIdAsGuid(contentKeyData.Id));

                foreach (IngestManifestFileData file in asset.IngestManifestFiles)
                {
                    ulong iv = Convert.ToUInt64(file.InitializationVector, CultureInfo.InvariantCulture);
                    fileEncryption.SetInitializationVectorForFile(file.Name, iv);

                    FileInfo fileInfo = null;
                    fileInfo = TrackedFilesPaths.ContainsKey(file.Id) ? new FileInfo(TrackedFilesPaths[file.Id]) : new FileInfo(file.Name);

                    string destinationPath = Path.Combine(outputPath, fileInfo.Name);
                    if (File.Exists(destinationPath))
                    {
                        if (overwriteExistingEncryptedFiles)
                        {
                            File.Delete(destinationPath);
                        }
                        else
                        {
                            throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, StringTable.BulkIngestFileExists, destinationPath));
                        }
                    }

                    long fileSize = fileInfo.Length;
                    int maxBlockSize = GetBlockSize(fileSize);

                    int numThreads = MaxNumberOfEncryptionThreadsForFilePerCore*Environment.ProcessorCount;
                    ConcurrentQueue<Tuple<int, int>> queue = PrepareUploadQueue(maxBlockSize, fileSize);
                    if (queue.Count < numThreads)
                    {
                        numThreads = queue.Count;
                    }

                    File.Create(destinationPath).Dispose();

                    Action action = GetEncryptionAction(cancellationToken, fileEncryption, file, destinationPath, fileInfo, queue, maxBlockSize);
                    for (int i = 0; i < numThreads; i++)
                    {
                        encryptTasks.Add(Task.Factory.StartNew((action), cancellationToken));
                    }

                }
                try
                {
                    Task.WaitAll(encryptTasks.ToArray());
                }
                finally
                {
                    fileEncryption.Dispose();
                }
            }
        }