Ejemplo n.º 1
0
        private async Task CreateStreamingJobAsync(long id, string url, CancellationToken token)
        {
            var videoAsset = await CreateOutputAssetAsync(id.ToString(), AssetType.Long, token);

            var jobInput =
                new JobInputHttp(files: new[] { url });

            JobOutput[] jobOutputs =
            {
                new JobOutputAsset(videoAsset.Name, label: AssetType.Long.ToString()),
            };

            var v = await _context;

            // If you already have a job with the desired name, use the Jobs.Get method
            // to get the existing job. In Media Services v3, Get methods on entities returns null
            // if the entity doesn't exist (a case-insensitive check on the name).
            try
            {
                await v.Jobs.CreateAsync(
                    _config.ResourceGroup,
                    _config.AccountName,
                    StreamingTransformer,
                    $"{id} video",
                    new Job
                {
                    Input   = jobInput,
                    Outputs = jobOutputs,
                }, token);
            }
            catch (ApiErrorException e) when(e.Response.StatusCode == HttpStatusCode.Conflict)
            {
                //There is already a job on this
            }
        }
Ejemplo n.º 2
0
        private static async Task <Job> SubmitJobAsync(string inputBlobName, string outputAssetName, IAzureMediaServicesClient client, string jobName, ILogger log, Dictionary <string, string> metadata)
        {
            try
            {
                JobInputHttp jobInput   = new JobInputHttp(files: new[] { inputBlobName });
                JobOutput[]  jobOutputs = { new JobOutputAsset(outputAssetName) };

                var metaString = JsonConvert.SerializeObject(metadata);
                Dictionary <string, string> metaDic = new Dictionary <string, string>();
                metaDic.Add("metaString", metaString);
                // In this example, we are assuming that the job name is unique.
                //
                // If you already have a job with the desired name, use the Jobs.Get method
                // to get the existing job. In Media Services v3, Get methods on entities returns null
                // if the entity doesn't exist (a case-insensitive check on the name).
                Job job = await client.Jobs.CreateAsync(
                    ResourceGroup,
                    AccountName,
                    TransformName,
                    jobName,
                    new Job
                {
                    CorrelationData = metaDic,
                    Input           = jobInput,
                    Outputs         = jobOutputs,
                });

                return(job);
            }
            catch (Exception ex)
            {
                log.LogError($"Error with creating Job: {ex.Message}");
                throw;
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Generates test data
        /// </summary>
        /// <param name="sequenceNumber">sequence number</param>
        /// <param name="uniqueness">unique part of the names</param>
        /// <returns>Generated JobRequestModel</returns>
        private static JobRequestModel GenerateJobRequestModel(int sequenceNumber, string uniqueness)
        {
            var jobId           = $"jobId-{sequenceNumber}-{uniqueness}";
            var jobName         = $"jobName-{sequenceNumber}-{uniqueness}";
            var outputAssetName = $"output-{sequenceNumber}-{uniqueness}";

            // Add job input for this test
            var input = new JobInputHttp(
                baseUri: "<Enter URL>",
                files: new List <string> {
                "<Enter Filename>"
            },
                label: "input1"
                );

            var request = new JobRequestModel
            {
                Id              = jobId,
                JobName         = jobName,
                OutputAssetName = outputAssetName,
                TransformName   = transformName,
                JobInputs       = new JobInputs
                {
                    Inputs = new List <JobInput> {
                        input
                    }
                }
            };

            return(request);
        }
Ejemplo n.º 4
0
        private async Task <Job> SubmitJobAsync(IAzureMediaServicesClient client,
                                                string resourceGroup,
                                                string accountName,
                                                string transformName,
                                                string outputAssetName,
                                                string jobName,
                                                string url)
        {
            JobInputHttp jobInput =
                new JobInputHttp(files: new[] { url });

            JobOutput[] jobOutputs =
            {
                new JobOutputAsset(outputAssetName),
            };

            Job job = await client.Jobs.CreateAsync(
                resourceGroup,
                accountName,
                transformName,
                jobName,
                new Job
            {
                Input   = jobInput,
                Outputs = jobOutputs,
            });

            return(job);
        }
        /// <summary>
        /// Submits a request to Media Services to apply the specified Transform to a given input video.
        /// </summary>
        /// <param name="client">The Media Services client.</param>
        /// <param name="resourceGroupName">The name of the resource group within the Azure subscription.</param>
        /// <param name="accountName"> The Media Services account name.</param>
        /// <param name="transformName">The name of the transform.</param>
        /// <param name="outputAssetName">The (unique) name of the  output asset that will store the result of the encoding job. </param>
        /// <param name="jobName">The (unique) name of the job.</param>
        /// <returns></returns>
        private static Job SubmitJob(IAzureMediaServicesClient client,
                                     string resourceGroup,
                                     string accountName,
                                     string transformName,
                                     string outputAssetName,
                                     string jobName)
        {
            // This example shows how to encode from any HTTPs source URL - a new feature of the v3 API.
            // Change the URL to any accessible HTTPs URL or SAS URL from Azure.
            JobInputHttp jobInput =
                new JobInputHttp(files: new[] { "https://nimbuscdn-nimbuspm.streaming.mediaservices.windows.net/2b533311-b215-4409-80af-529c3e853622/Ignite-short.mp4" });

            JobOutput[] jobOutputs =
            {
                new JobOutputAsset(outputAssetName),
            };

            Job job = client.Jobs.Create(
                resourceGroup, accountName,
                transformName,
                jobName,
                new Job
            {
                Input   = jobInput,
                Outputs = jobOutputs,
            });

            return(job);
        }
Ejemplo n.º 6
0
        public async Task CreateStudyRoomSessionEncoding(string sessionId, string url,
                                                         CancellationToken token)
        {
            var thumbnailAsset = await CreateOutputAssetAsync(sessionId, AssetType.StudyRoom, token);

            var jobInput =
                new JobInputHttp(files: new[] { url });

            JobOutput[] jobOutputs =
            {
                new JobOutputAsset(thumbnailAsset.Name, label: AssetType.StudyRoom.ToString())
            };
            var v = await _context;

            try
            {
                await v.Jobs.CreateAsync(
                    _config.ResourceGroup,
                    _config.AccountName,
                    FullHdTransformer,
                    $"{sessionId} study room session",
                    new Job
                {
                    Input   = jobInput,
                    Outputs = jobOutputs,
                }, token);
            }
            catch (ApiErrorException e) when(e.Response.StatusCode == HttpStatusCode.Conflict)
            {
                //Do nothing
            }
        }
        // </CreateOutputAsset>

        /// <summary>
        /// Submits a request to Media Services to apply the specified Transform to a given input video.
        /// </summary>
        /// <param name="client">The Media Services client.</param>
        /// <param name="resourceGroupName">The name of the resource group within the Azure subscription.</param>
        /// <param name="accountName"> The Media Services account name.</param>
        /// <param name="transformName">The name of the transform.</param>
        /// <param name="outputAssetName">The (unique) name of the  output asset that will store the result of the encoding job. </param>
        /// <param name="jobName">The (unique) name of the job.</param>
        /// <returns></returns>
        // <SubmitJob>
        private static async Task <Job> SubmitJobAsync(IAzureMediaServicesClient client,
                                                       string resourceGroup,
                                                       string accountName,
                                                       string transformName,
                                                       string outputAssetName,
                                                       string jobName)
        {
            // This example shows how to encode from any HTTPs source URL - a new feature of the v3 API.
            // Change the URL to any accessible HTTPs URL or SAS URL from Azure.
            JobInputHttp jobInput =
                new JobInputHttp(files: new[] { "https://nimbuscdn-nimbuspm.streaming.mediaservices.windows.net/2b533311-b215-4409-80af-529c3e853622/Ignite-short.mp4" });

            JobOutput[] jobOutputs =
            {
                new JobOutputAsset(outputAssetName),
            };

            // In this example, we are assuming that the job name is unique.
            //
            // If you already have a job with the desired name, use the Jobs.Get method
            // to get the existing job. In Media Services v3, the Get method on entities returns null
            // if the entity doesn't exist (a case-insensitive check on the name).
            Job job = await client.Jobs.CreateAsync(
                resourceGroup,
                accountName,
                transformName,
                jobName,
                new Job
            {
                Input   = jobInput,
                Outputs = jobOutputs,
            });

            return(job);
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Submits a request to Media Services to apply the specified Transform to a given input video.
        /// </summary>
        /// <param name="client">The Media Services client.</param>
        /// <param name="resourceGroup">The name of the resource group within the Azure subscription.</param>
        /// <param name="accountName"> The Media Services account name.</param>
        /// <param name="transformName">The name of the transform.</param>
        /// <param name="outputAssetName">The (unique) name of the  output asset that will store the result of the encoding job. </param>
        /// <param name="jobName">The (unique) name of the job.</param>
        /// <returns></returns>
        private static async Task <Job> SubmitJobAsync(IAzureMediaServicesClient client,
                                                       string resourceGroup,
                                                       string accountName,
                                                       string transformName,
                                                       string outputAssetName,
                                                       string jobName)
        {
            // This example shows how to encode from any HTTPs source URL - a new feature of the v3 API.
            // Change the URL to any accessible HTTPs URL or SAS URL from Azure.
            JobInputHttp jobInput =
                new JobInputHttp(files: new[] { "https://nimbuscdn-nimbuspm.streaming.mediaservices.windows.net/2b533311-b215-4409-80af-529c3e853622/Ignite-short.mp4" });

            JobOutput[] jobOutputs =
            {
                new JobOutputAsset(outputAssetName),
            };

            // In this example, we are assuming that the job name is unique.
            // If you already have a job with the desired name, use the Jobs.Get method
            // to get the existing job. In Media Services v3, the Get method on entities returns null
            // if the entity doesn't exist (a case-insensitive check on the name).
            Job job;

            try
            {
                Console.WriteLine("Creating a job...");
                job = await client.Jobs.CreateAsync(
                    resourceGroup,
                    accountName,
                    transformName,
                    jobName,
                    new Job
                {
                    Input   = jobInput,
                    Outputs = jobOutputs,
                }
                    );
            }
            catch (Exception exception)
            {
                if (exception.GetBaseException() is ApiErrorException apiException)
                {
                    Console.Error.WriteLine(
                        $"ERROR: API call failed with error code '{apiException.Body.Error.Code}' and message '{apiException.Body.Error.Message}'.");
                }
                throw exception;
            }

            return(job);
        }
Ejemplo n.º 9
0
        private static void VerifyJobInput(JobInput expectedInput, JobInput actualInput, bool jobInputsAcceptable = true)
        {
            Type expectedInputType = expectedInput.GetType();

            Assert.Equal(expectedInputType, actualInput.GetType());

            if (typeof(JobInputAsset) == expectedInputType)
            {
                JobInputAsset expected = (JobInputAsset)expectedInput;
                JobInputAsset actual   = (JobInputAsset)actualInput;

                Assert.Equal(expected.AssetName, actual.AssetName);
            }
            else if (typeof(JobInputHttp) == expectedInputType)
            {
                JobInputHttp expected = (JobInputHttp)expectedInput;
                JobInputHttp actual   = (JobInputHttp)actualInput;

                Assert.Equal(expected.BaseUri, actual.BaseUri);
                Assert.Equal(expected.Label, actual.Label);
                Assert.Equal(expected.Files.Count, actual.Files.Count);

                for (int i = 0; i < expected.Files.Count; i++)
                {
                    Assert.Equal(expected.Files[i], actual.Files[i]);
                }
            }
            else if (typeof(JobInputs) == expectedInputType)
            {
                if (!jobInputsAcceptable)
                {
                    throw new InvalidOperationException("Only top level JobInputs are supported.");
                }

                JobInputs expected = (JobInputs)expectedInput;
                JobInputs actual   = (JobInputs)actualInput;

                Assert.Equal(expected.Inputs.Count, actual.Inputs.Count);

                for (int i = 0; i < expected.Inputs.Count; i++)
                {
                    VerifyJobInput(expected.Inputs[i], actual.Inputs[i], false);
                }
            }
            else
            {
                throw new InvalidOperationException($"Unexpected input type {expectedInputType.Name}");
            }
        }
Ejemplo n.º 10
0
        private async Task <List <Uri> > GenerateVideoThumbnailAsync(string fileUrl, string thumbnailStoragePath, string resourceGroup, string accountName)
        {
            pAzureMediaServicesClient.LongRunningOperationRetryTimeout = 2;
            var streamUris = new List <Uri>();

            try
            {
                // Ensure that you have customized encoding Transform.  This is really a one time setup operation.
                Transform adaptiveEncodeTransform = await EnsureTransformExistsAsync(pAzureMediaServicesClient, resourceGroup, accountName, fileUrl);

                // Creating a unique suffix so that we don't have name collisions if you run the sample
                // multiple times without cleaning up.
                var uniqueness      = Guid.NewGuid().ToString().Substring(0, 13);
                var jobName         = "job-" + uniqueness;
                var inputAssetName  = "input-" + uniqueness;
                var outputAssetName = "output-" + uniqueness;

                var         input      = new JobInputHttp(files: new[] { fileUrl });
                Asset       asetInput  = new Asset();
                JobOutput[] jobOutputs = { new JobOutputAsset(outputAssetName) };

                await pAzureMediaServicesClient.Assets.CreateOrUpdateAsync(resourceGroup, accountName, outputAssetName, asetInput);

                Job job = await pAzureMediaServicesClient.Jobs.CreateAsync(resourceGroup, accountName, fileUrl, jobName, new Job { Input = input, Outputs = jobOutputs });

                job = await pAzureMediaServicesClient.Jobs.GetAsync(resourceGroup, accountName, fileUrl, jobName);

                var feedItemGuid = pBlobHelper.GetFileGuidFromFilePath(fileUrl);

                if (job.State == JobState.Finished)
                {
                    streamUris = await GetStreamingUrlsAsync(pAzureMediaServicesClient, resourceGroup, accountName, feedItemGuid.ToString());
                }
            }
            catch (ApiErrorException ex)
            {
                string code    = ex.Body.Error.Code;
                string message = ex.Body.Error.Message;
            }

            return(streamUris);
        }
Ejemplo n.º 11
0
        private async void listBoxInput_SelectedIndexChanged(object sender, EventArgs e)
        {
            dataGridInput.Rows.Clear();

            if (MyJob.Input.GetType() == typeof(JobInputAsset))
            {
                JobInputAsset inputA = MyJob.Input as JobInputAsset;
                dataGridInput.Rows.Add("Input type", "asset");
                dataGridInput.Rows.Add("Asset name", inputA.AssetName);
                dataGridInput.Rows.Add("Asset type", (await AssetInfo.GetAssetTypeAsync(inputA.AssetName, _amsClient))?.Type);
                if (inputA.Start != null && inputA.Start.GetType() == typeof(AbsoluteClipTime))
                {
                    AbsoluteClipTime startA = inputA.Start as AbsoluteClipTime;
                    dataGridInput.Rows.Add("Absolute Clip Time Start", startA.Time.ToString());
                }
                if (inputA.End != null && inputA.End.GetType() == typeof(AbsoluteClipTime))
                {
                    AbsoluteClipTime endA = inputA.End as AbsoluteClipTime;
                    dataGridInput.Rows.Add("Absolute Clip Time End", endA.Time.ToString());
                }
                dataGridInput.Rows.Add("Label", inputA.Label);
                dataGridInput.Rows.Add("Files", string.Join(Constants.endline, inputA.Files));
            }
            else if (MyJob.Input.GetType() == typeof(JobInputHttp))
            {
                JobInputHttp inputH = MyJob.Input as JobInputHttp;
                dataGridInput.Rows.Add("Input type", "http");
                dataGridInput.Rows.Add("Base Url", inputH.BaseUri);
                if (inputH.Start != null && inputH.Start.GetType() == typeof(AbsoluteClipTime))
                {
                    AbsoluteClipTime startA = inputH.Start as AbsoluteClipTime;
                    dataGridInput.Rows.Add("Absolute Clip Time Start", startA.Time.ToString());
                }
                if (inputH.End != null && inputH.End.GetType() == typeof(AbsoluteClipTime))
                {
                    AbsoluteClipTime endA = inputH.End as AbsoluteClipTime;
                    dataGridInput.Rows.Add("Absolute Clip Time End", endA.Time.ToString());
                }
                dataGridInput.Rows.Add("Label", inputH.Label);
                dataGridInput.Rows.Add("Files", string.Join(Constants.endline, inputH.Files));
            }
        }
Ejemplo n.º 12
0
        private JobInput GetJobInput(MediaJob mediaJob)
        {
            JobInput jobInput;

            if (!string.IsNullOrEmpty(mediaJob.InputFileUrl))
            {
                jobInput = new JobInputHttp()
                {
                    Files = new string[] { mediaJob.InputFileUrl }
                };
            }
            else
            {
                jobInput = new JobInputAsset()
                {
                    AssetName = mediaJob.InputAssetName
                };
            }
            return(jobInput);
        }
Ejemplo n.º 13
0
        public async Task <Job> CreateEncodeJobAsync(IZeroBlob original, string encodedFileName, int contentId, CancellationToken cancellationToken)
        {
            var extension = Path.GetExtension(original.GetName());

            if (extension == null || !_settings.SupportedVideoTypes.Contains(extension.ToLower()))
            {
                throw new NotSupportedException("Video type not supported");
            }
            string uniqueness      = $"{contentId}-{Guid.NewGuid().ToString("N").Substring(0, 10)}";
            string jobName         = $"job-{uniqueness}";
            string outputAssetName = $"output-{uniqueness}";
            var    output          = new Asset(description: $"{contentId}-{encodedFileName}");
            var    outPutAsset     = await _client.Assets.CreateOrUpdateAsync(_settings.ResourceGroup, _settings.AccountName, outputAssetName, output, cancellationToken);

            JobOutput[] jobOutputs =
            {
                new JobOutputAsset(outPutAsset.Name)
            };
            var jobInput = new JobInputHttp
            {
                Files = new[] { original.GetReadSharedAccessUrl("*") }
            };
            IDictionary <string, string> correlationData = null; // Add custom data sent with the job. Can then be used when processing it.
            var job = await _client.Jobs.CreateAsync(
                _settings.ResourceGroup,
                _settings.AccountName,
                _settings.MediaServicesTransform,
                jobName,
                new Job
            {
                Input           = jobInput,
                Outputs         = jobOutputs,
                CorrelationData = correlationData
            }
                , cancellationToken);

            return(job);
        }
Ejemplo n.º 14
0
        public async Task CreatePreviewJobAsync(long id, string url, TimeSpan videoLength, CancellationToken token)
        {
            var clipEndTime = Math.Min(TimeSpan.FromSeconds(30).Ticks, videoLength.Ticks);
            var videoAsset  = await CreateOutputAssetAsync(id.ToString(), AssetType.Short, token);

            var jobInput =
                new JobInputHttp(files: new[] { url }, end: new AbsoluteClipTime(new TimeSpan(clipEndTime)));

            JobOutput[] jobOutputs =
            {
                new JobOutputAsset(videoAsset.Name, label: AssetType.Short.ToString()),
            };

            var v = await _context;

            // If you already have a job with the desired name, use the Jobs.Get method
            // to get the existing job. In Media Services v3, Get methods on entities returns null
            // if the entity doesn't exist (a case-insensitive check on the name).
            try
            {
                await v.Jobs.CreateAsync(
                    _config.ResourceGroup,
                    _config.AccountName,
                    StreamingTransformer,
                    $"{id} preview",
                    new Job
                {
                    Input   = jobInput,
                    Outputs = jobOutputs,
                }, token);
            }
            catch (ApiErrorException e) when(e.Response.StatusCode == HttpStatusCode.Conflict)
            {
                //There is already a job on this
            }
        }
Ejemplo n.º 15
0
        static void Main(string[] args)
        {
            ConfigWrapper             config = new ConfigWrapper();
            IAzureMediaServicesClient client = CreateMediaServicesClient(config);

            try
            {
                // Ensure that you have customized encoding Transform.  This is really a one time setup operation.
                Transform adaptiveEncodeTransform = EnsureTransformExists(client, config.Region, transformName);

                // Creating a unique suffix so that we don't have name collisions if you run the sample
                // multiple times without cleaning up.
                string uniqueness      = Guid.NewGuid().ToString().Substring(0, 13);
                string jobName         = "job-" + uniqueness;
                string inputAssetName  = "input-" + uniqueness;
                string outputAssetName = "output-" + uniqueness;


                Asset asset = client.Assets.CreateOrUpdate(resourceGroupName, accountName, inputAssetName, new Asset());

                var input = new JobInputHttp(
                    baseUri: "https://nimbuscdn-nimbuspm.streaming.mediaservices.windows.net/2b533311-b215-4409-80af-529c3e853622/",
                    files: new List <String> {
                    "Ignite-short.mp4"
                }
                    );


                CreateOutputAsset(client, outputAssetName);

                Job job = SubmitJob(client, transformName, jobName, input, outputAssetName);

                DateTime startedTime = DateTime.Now;

                job = WaitForJobToFinish(client, transformName, jobName);

                TimeSpan elapsed = DateTime.Now - startedTime;

                if (job.State == JobState.Finished)
                {
                    Console.WriteLine("Job finished.");
                    if (!Directory.Exists(outputFolder))
                    {
                        Directory.CreateDirectory(outputFolder);
                    }
                    DownloadResults(client, outputAssetName, outputFolder).Wait();
                }
                else if (job.State == JobState.Error)
                {
                    Console.WriteLine($"ERROR: Job finished with error message: {job.Outputs[0].Error.Message}");
                    Console.WriteLine($"ERROR:                   error details: {job.Outputs[0].Error.Details[0].Message}");
                }
            }
            catch (ApiErrorException ex)
            {
                string code    = ex.Body.Error.Code;
                string message = ex.Body.Error.Message;

                Console.WriteLine("ERROR:API call failed with error code: {0} and message: {1}", code, message);
            }
        }
        /// <summary>
        /// Run the sample async.
        /// </summary>
        /// <param name="config">This param is of type ConfigWrapper, which reads values from local configuration file.</param>
        /// <returns>A task.</returns>
        private static async Task RunAsync(ConfigWrapper config)
        {
            IAzureMediaServicesClient client;

            try
            {
                client = await CreateMediaServicesClientAsync(config);
            }
            catch (Exception e)
            {
                if (e.Source.Contains("ActiveDirectory"))
                {
                    Console.Error.WriteLine("TIP: Make sure that you have filled out the appsettings.json file before running this sample.");
                    Console.Error.WriteLine();
                }
                Console.Error.WriteLine($"{e.Message}");
                return;
            }

            // Set the polling interval for long running operations to 2 seconds.
            // The default value is 30 seconds for the .NET client SDK
            client.LongRunningOperationRetryTimeout = 2;

            // Creating a unique suffix so that we don't have name collisions if you run the sample
            // multiple times without cleaning up.
            string uniqueness      = Guid.NewGuid().ToString().Substring(0, 13);
            string jobName         = "job-" + uniqueness;
            string locatorName     = "locator-" + uniqueness;
            string outputAssetName = "output-" + uniqueness;
            bool   stopEndpoint    = false;

            try
            {
                // Ensure that you have customized encoding Transform.  This is really a one time setup operation.
                Transform adaptiveEncodeTransform = await EnsureTransformExists(client, config.ResourceGroup, config.AccountName,
                                                                                TransformName, preset : new BuiltInStandardEncoderPreset(EncoderNamedPreset.AdaptiveStreaming));

                var input = new JobInputHttp(
                    baseUri: "https://nimbuscdn-nimbuspm.streaming.mediaservices.windows.net/2b533311-b215-4409-80af-529c3e853622/",
                    files: new List <String> {
                    "Ignite-short.mp4"
                },
                    label: "input1"
                    );

                // Output from the encoding Job must be written to an Asset, so let's create one. Note that we
                // are using a unique asset name, there should not be a name collision.
                Asset outputAsset = await CreateOutputAssetAsync(client, config.ResourceGroup, config.AccountName, outputAssetName);

                Job job = await SubmitJobAsync(client, config.ResourceGroup, config.AccountName, TransformName, jobName, input, outputAsset.Name);

                DateTime startedTime = DateTime.Now;

                // In this demo code, we will poll for Job status. Polling is not a recommended best practice for production
                // applications because of the latency it introduces. Overuse of this API may trigger throttling. Developers
                // should instead use Event Grid. To see how to implement the event grid, see the sample
                // https://github.com/Azure-Samples/media-services-v3-dotnet/tree/master/ContentProtection/BasicAESClearKey.
                job = WaitForJobToFinish(client, config.ResourceGroup, config.AccountName, TransformName, jobName);

                TimeSpan elapsed = DateTime.Now - startedTime;
                Console.WriteLine($"Job elapsed time: {elapsed}");

                if (job.State == JobState.Finished)
                {
                    Console.WriteLine("Job finished.");

                    // Now that the content has been encoded, publish it for Streaming by creating
                    // a StreamingLocator.
                    StreamingLocator locator = await CreateStreamingLocatorAsync(client, config.ResourceGroup, config.AccountName,
                                                                                 outputAsset.Name, locatorName);

                    StreamingEndpoint streamingEndpoint = await client.StreamingEndpoints.GetAsync(config.ResourceGroup, config.AccountName,
                                                                                                   DefaultStreamingEndpointName);

                    if (streamingEndpoint != null)
                    {
                        if (streamingEndpoint.ResourceState != StreamingEndpointResourceState.Running)
                        {
                            await client.StreamingEndpoints.StartAsync(config.ResourceGroup, config.AccountName, DefaultStreamingEndpointName);

                            // We started the endpoint, we should stop it in cleanup.
                            stopEndpoint = true;
                        }
                    }

                    IList <string> urls = await GetStreamingUrlsAsync(client, config.ResourceGroup, config.AccountName, locator.Name, streamingEndpoint);

                    foreach (var url in urls)
                    {
                        Console.WriteLine(url);
                        Console.WriteLine();
                    }

                    Console.WriteLine("To stream, copy and paste the Streaming URL into the Azure Media Player at 'http://aka.ms/azuremediaplayer'.");
                    Console.WriteLine("When finished, press ENTER to continue.");
                    Console.WriteLine();
                    Console.Out.Flush();
                    Console.ReadLine();

                    // Download output asset for verification.
                    Console.WriteLine("Downloading output asset...");
                    Console.WriteLine();
                    if (!Directory.Exists(OutputFolder))
                    {
                        Directory.CreateDirectory(OutputFolder);
                    }
                    DownloadResults(client, config.ResourceGroup, config.AccountName, outputAsset.Name, OutputFolder).Wait();

                    Console.WriteLine("Please check the files in the output folder.");
                    Console.WriteLine("When finished, press ENTER to cleanup.");
                    Console.Out.Flush();
                    Console.ReadLine();
                }
                else if (job.State == JobState.Error)
                {
                    Console.WriteLine($"ERROR: Job finished with error message: {job.Outputs[0].Error.Message}");
                    Console.WriteLine($"ERROR:                   error details: {job.Outputs[0].Error.Details[0].Message}");
                }
            }
            catch (ApiErrorException e)
            {
                Console.WriteLine("Hit ApiErrorException");
                Console.WriteLine($"\tCode: {e.Body.Error.Code}");
                Console.WriteLine($"\tMessage: {e.Body.Error.Message}");
                Console.WriteLine();
                Console.WriteLine("Exiting, cleanup may be necessary...");
                Console.ReadLine();
            }
            finally
            {
                await CleanUpAsync(client, config.ResourceGroup, config.AccountName, TransformName, jobName, outputAssetName, locatorName,
                                   stopEndpoint, DefaultStreamingEndpointName);

                Console.WriteLine("Done.");
            }
        }
Ejemplo n.º 17
0
        public static async Task <HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req, FunctionContext executionContext)
        {
            var log = executionContext.GetLogger("SubmitEncodingJob");

            log.LogInformation("C# HTTP trigger function processed a request.");

            // Get request body data.
            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            var    data        = (RequestBodyModel)JsonConvert.DeserializeObject(requestBody, typeof(RequestBodyModel));

            // Return bad request if input asset name is not passed in
            if (data.InputAssetName == null && data.InputUrl == null)
            {
                return(HttpRequest.ResponseBadRequest(req, "Please pass inputAssetName or inputUrl in the request body"));
            }

            // Return bad request if input asset name is not passed in
            if (data.TransformName == null)
            {
                return(HttpRequest.ResponseBadRequest(req, "Please pass transformName in the request body"));
            }

            ConfigWrapper config = ConfigUtils.GetConfig();

            IAzureMediaServicesClient client;

            try
            {
                client = await Authentication.CreateMediaServicesClientAsync(config);

                log.LogInformation("AMS Client created.");
            }
            catch (Exception e)
            {
                if (e.Source.Contains("ActiveDirectory"))
                {
                    log.LogError("TIP: Make sure that you have filled out the appsettings.json file before running this sample.");
                }
                log.LogError($"{e.Message}");

                return(HttpRequest.ResponseBadRequest(req, e.Message));
            }

            // Set the polling interval for long running operations to 2 seconds.
            // The default value is 30 seconds for the .NET client SDK
            client.LongRunningOperationRetryTimeout = 2;

            // Creating a unique suffix so that we don't have name collisions if you run the sample
            // multiple times without cleaning up.
            string uniqueness      = Guid.NewGuid().ToString().Substring(0, 13);
            string jobName         = $"job-{uniqueness}";
            string outputAssetName = $"output-{uniqueness}";

            Transform transform;

            try
            {
                // Ensure that you have the encoding Transform.  This is really a one time setup operation.
                transform = await TransformUtils.CreateEncodingTransform(client, log, config.ResourceGroup, config.AccountName, data.TransformName, data.BuiltInPreset);

                log.LogInformation("Transform retrieved.");
            }
            catch (ErrorResponseException ex)
            {
                return(HttpRequest.ResponseBadRequest(req, LogUtils.LogError(log, ex, "Error when creating the transform.")));
            }

            Asset outputAsset;

            try
            {
                // Output from the job must be written to an Asset, so let's create one
                outputAsset = await AssetUtils.CreateAssetAsync(client, log, config.ResourceGroup, config.AccountName, outputAssetName, data.OutputAssetStorageAccount);

                log.LogInformation($"Output asset '{outputAssetName}' created.");
            }
            catch (ErrorResponseException ex)
            {
                return(HttpRequest.ResponseBadRequest(req, LogUtils.LogError(log, ex, "Error when creating the output asset.")));
            }

            // Job input prepration : asset or url
            JobInput jobInput;

            if (data.InputUrl != null)
            {
                jobInput = new JobInputHttp(files: new[] { data.InputUrl });
                log.LogInformation("Input is a Url.");
            }
            else
            {
                jobInput = new JobInputAsset(assetName: data.InputAssetName);
                log.LogInformation($"Input is asset '{data.InputAssetName}'.");
            }

            Job job;

            try
            {
                // Job submission to Azure Media Services
                job = await JobUtils.SubmitJobAsync(
                    client,
                    log,
                    config.ResourceGroup,
                    config.AccountName,
                    data.TransformName,
                    jobName,
                    jobInput,
                    outputAssetName
                    );

                log.LogInformation($"Job '{jobName}' submitted.");
            }
            catch (ErrorResponseException ex)
            {
                return(HttpRequest.ResponseBadRequest(req, LogUtils.LogError(log, ex, "Error when submitting the job.")));
            }


            AnswerBodyModel dataOk = new()
            {
                OutputAssetName = outputAsset.Name,
                JobName         = job.Name
            };

            return(HttpRequest.ResponseOk(req, dataOk, HttpStatusCode.Accepted));
        }
    }
Ejemplo n.º 18
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation($"AMS v3 Function - CopyUrlToStorage was triggered!");

            string  requestBody = new StreamReader(req.Body).ReadToEnd();
            dynamic data        = JsonConvert.DeserializeObject(requestBody);

            // Set up ENV variables
            string accountName   = System.Environment.GetEnvironmentVariable("AccountName");
            string resourceGroup = System.Environment.GetEnvironmentVariable("ResourceGroup");
            MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper();

            // Transform & Job
            string transformName = "abrTransform";
            string jobName       = "job-" + Guid.NewGuid();

            // Asset & Url
            string remoteUrl = Convert.ToString(data.remoteUrl);
            string assetName = Convert.ToString(data.assetName);

            try
            {
                IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig);

                // Encode from any HTTPs source URL - a new feature of the v3 API.
                JobInputHttp jobInput =
                    new JobInputHttp(files: new List <string> {
                    remoteUrl
                });

                JobOutput[] jobOutputs =
                {
                    new JobOutputAsset(assetName),
                };

                // In this function, we are ensuring that the job name is unique.
                Job job = await client.Jobs.CreateAsync(
                    resourceGroup,
                    accountName,
                    transformName,
                    jobName,
                    new Job
                {
                    Input   = jobInput,
                    Outputs = jobOutputs,
                });
            }
            catch (ApiErrorException e)
            {
                log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}");
                return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message));
            }
            catch (Exception e)
            {
                log.LogError($"ERROR: Exception with message: {e.Message}");
                return(new BadRequestObjectResult("Error: " + e.Message + " caused by " + e.StackTrace));
            }

            return((ActionResult) new OkObjectResult(new
            {
                assetName = assetName
            }));
        }
Ejemplo n.º 19
0
        public async Task <TextModerationResult> ModerateVideo(string videoPath)
        {
            string resourceGroup = this.textModeratorConfig.ResourceGroup;
            string accountName   = this.textModeratorConfig.AccountName;

            IAzureMediaServicesClient client = await CreateMediaServicesClientAsync();

            // Set the polling interval for long running operations to 2 seconds.
            // The default value is 30 seconds for the .NET client SDK
            client.LongRunningOperationRetryTimeout = 2;
            TextModerationResult result = null;

            try
            {
                string uniqueness = Guid.NewGuid().ToString();
                string jobName    = JobNamePrefix + uniqueness;
                string streamingOutputAssetName = textModeratorConfig + uniqueness;
                string analyticsOutputAssetName = textModeratorConfig + uniqueness;

                JobInput     jobInput               = null;
                List <Asset> outputAssetList        = new List <Asset>();
                Transform    videoAnalyzerTransform = EnsureTransformExists(client, resourceGroup, accountName, VisualModerationTransformName);

                if (videoPath.StartsWith("http://") || videoPath.StartsWith("https://"))
                {
                    Uri    inputVideoUri = new Uri(videoPath);
                    string baseUri       = inputVideoUri.Scheme + "://" + inputVideoUri.Host;
                    for (int i = 0; i < inputVideoUri.Segments.Length - 1; i++)
                    {
                        baseUri = baseUri + inputVideoUri.Segments[i];
                    }
                    jobInput = new JobInputHttp(
                        baseUri: baseUri,
                        files: new List <String> {
                        inputVideoUri.Segments[inputVideoUri.Segments.Length - 1]
                    },
                        label: JobInputLabel
                        );
                }
                else
                {
                    string inputAssetName = InputAssetNamePrefix + uniqueness;
                    CreateInputAssetWithUploading(client, resourceGroup, accountName, inputAssetName, videoPath).Wait();
                    jobInput = new JobInputAsset(assetName: inputAssetName, label: JobInputLabel);
                }

                List <JobOutput> jobOutputList        = new List <JobOutput>();
                Asset            analyticsOutputAsset = CreateOutputAsset(client, resourceGroup, accountName, analyticsOutputAssetName);
                outputAssetList.Add(analyticsOutputAsset);
                jobOutputList.Add(new JobOutputAsset(assetName: analyticsOutputAsset.Name, label: JobAnalyticsOutputLabel));
                if (this.textModeratorConfig.EnableStreamingVideo)
                {
                    Asset streamingOutputAsset = CreateOutputAsset(client, resourceGroup, accountName, streamingOutputAssetName);
                    outputAssetList.Add(streamingOutputAsset);
                    jobOutputList.Add(new JobOutputAsset(assetName: streamingOutputAsset.Name, label: JobVideoOutputLabel));
                }

                JobOutput[] jobOutputs = jobOutputList.ToArray();
                Job         job        = SubmitJob(client, resourceGroup, accountName, videoAnalyzerTransform.Name, jobName, jobInput, jobOutputs);
                job = WaitForJobToFinish(client, resourceGroup, accountName, videoAnalyzerTransform.Name, jobName);

                if (job.State == JobState.Finished)
                {
                    Console.WriteLine("\nAMSv3 Job finished.");
                    List <StreamingLocator> locators = PublishAssets(client, resourceGroup, accountName, jobOutputList);
                    result = CreateVisualModeratorResult(client, resourceGroup, accountName, videoPath, locators);

                    using (var webClient = new WebClient())
                    {
                        webClient.Encoding          = Encoding.UTF8;
                        result.VisualModerationJson = webClient.DownloadString(result.StreamingUrlDetails.ContentModerationJsonUrl);
                        result.OcrJson = webClient.DownloadString(result.StreamingUrlDetails.OcrJsonUrl);
                    }
                }
                else if (job.State == JobState.Error)
                {
                    Console.WriteLine($"ERROR: Job finished with error message: {job.Outputs[0].Error.Message}");
                    Console.WriteLine($"ERROR:                   error details: {job.Outputs[0].Error.Details[0].Message}");
                }
            }
            catch (ApiErrorException ex)
            {
                string code    = ex.Body.Error.Code;
                string message = ex.Body.Error.Message;

                Console.WriteLine("ERROR:API call failed with error code: {0} and message: {1}", code, message);
            }

            return(result);
        }
Ejemplo n.º 20
0
        /// <summary>
        /// Run the sample async.
        /// </summary>
        /// <param name="config">The parm is of type ConfigWrapper. This class reads values from local configuration file.</param>
        /// <returns></returns>
        // <RunAsync>
        private static async Task RunAsync(ConfigWrapper config)
        {
            IAzureMediaServicesClient client = await CreateMediaServicesClientAsync(config);

            // Set the polling interval for long running operations to 2 seconds.
            // The default value is 30 seconds for the .NET client SDK
            client.LongRunningOperationRetryTimeout = 2;

            try
            {
                // Generate a new random token signing key to use
                RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
                rng.GetBytes(TokenSigningKey);

                //Create the content key policy that configures how the content key is delivered to end clients
                // via the Key Delivery component of Azure Media Services.
                ContentKeyPolicy policy = EnsureContentKeyPolicyExists(client, config.ResourceGroup, config.AccountName, ContentKeyPolicyName);

                // Ensure that you have customized encoding Transform.  This is really a one time setup operation.
                Transform adaptiveEncodeTransform = EnsureTransformExists(client, config.ResourceGroup, config.AccountName, transformName, preset: new BuiltInStandardEncoderPreset(EncoderNamedPreset.AdaptiveStreaming));

                // Creating a unique suffix so that we don't have name collisions if you run the sample
                // multiple times without cleaning up.
                string uniqueness = Guid.NewGuid().ToString().Substring(0, 13);

                string jobName              = "job-" + uniqueness;
                string inputAssetName       = "input-" + uniqueness;
                string outputAssetName      = "output-" + uniqueness;
                string streamingLocatorName = "locator-" + uniqueness;

                Asset asset = client.Assets.CreateOrUpdate(config.ResourceGroup, config.AccountName, inputAssetName, new Asset());

                var input = new JobInputHttp(
                    baseUri: "https://nimbuscdn-nimbuspm.streaming.mediaservices.windows.net/2b533311-b215-4409-80af-529c3e853622/",
                    files: new List <String> {
                    "Ignite-short.mp4"
                },
                    label: "input1"
                    );


                Asset outputAsset = CreateOutputAsset(client, config.ResourceGroup, config.AccountName, outputAssetName);

                Job job = SubmitJob(client, config.ResourceGroup, config.AccountName, transformName, jobName, input, outputAsset.Name);

                DateTime startedTime = DateTime.Now;

                job = WaitForJobToFinish(client, config.ResourceGroup, config.AccountName, transformName, jobName);

                TimeSpan elapsed = DateTime.Now - startedTime;

                if (job.State == JobState.Finished)
                {
                    Console.WriteLine("Job finished.");

                    // Now that the content has been encoded, publish it for Streaming by creating
                    // a StreamingLocator.  Note that we are using one of the PredefinedStreamingPolicies
                    // which tell the Origin component of Azure Media Services how to publish the content
                    // for streaming.  In this case it applies AES Envelople encryption, which is also known
                    // ClearKey encryption (because the key is delivered to the playback client via HTTPS and
                    // not instead a DRM license).
                    StreamingLocator locator = new StreamingLocator(
                        assetName: outputAsset.Name,
                        streamingPolicyName: PredefinedStreamingPolicy.ClearKey,
                        defaultContentKeyPolicyName: ContentKeyPolicyName);

                    client.StreamingLocators.Create(config.ResourceGroup, config.AccountName, streamingLocatorName, locator);

                    // We are using the ContentKeyIdentifierClaim in the ContentKeyPolicy which means that the token presented
                    // to the Key Delivery Component must have the identifier of the content key in it.  Since we didn't specify
                    // a content key when creating the StreamingLocator, the system created a random one for us.  In order to
                    // generate our test token we must get the ContentKeyId to put in the ContentKeyIdentifierClaim claim.
                    var    response      = client.StreamingLocators.ListContentKeys(config.ResourceGroup, config.AccountName, streamingLocatorName);
                    string keyIdentifier = response.ContentKeys.First().Id.ToString();

                    // We can either use the "default" StreamingEndpoint or we can create a new StreamingEndpoint.
                    // Typically we would just ensure that the default endpoint was started but let's create one
                    // here to illustrate how it is done.

                    // Console.WriteLine($"Creating a streaming endpoint named {endpointName}");
                    // Console.WriteLine();

                    //StreamingEndpoint streamingEndpoint = new StreamingEndpoint(location: mediaService.Location);
                    var streamingEndpoint = client.StreamingEndpoints.Get(config.ResourceGroup, config.AccountName, "default");

                    // Get the URls to stream the output
                    var paths = client.StreamingLocators.ListPaths(config.ResourceGroup, config.AccountName, streamingLocatorName);

                    Console.WriteLine("The urls to stream the output from a client:");
                    Console.WriteLine();

                    var token = GetToken(Issuer, Audience, keyIdentifier, TokenSigningKey);

                    for (int i = 0; i < paths.StreamingPaths.Count; i++)
                    {
                        UriBuilder uriBuilder = new UriBuilder();
                        uriBuilder.Scheme = "https";
                        uriBuilder.Host   = streamingEndpoint.HostName;

                        if (paths.StreamingPaths[i].Paths.Count > 0)
                        {
                            //uriBuilder.Path = paths.StreamingPaths[i].Paths[0];
                            //Console.WriteLine($"\t{paths.StreamingPaths[i].StreamingProtocol}-{paths.StreamingPaths[i].EncryptionScheme}");
                            //Console.WriteLine($"\t\t{uriBuilder.ToString()}");
                            //Console.WriteLine();

                            // Look for just the DASH path and generate a URL for the Azure Media Player to playback the content with the AES token to decrypt.
                            // Note that the JWT token is set to expire in 1 hour.
                            if (paths.StreamingPaths[i].StreamingProtocol == StreamingPolicyStreamingProtocol.Dash)
                            {
                                uriBuilder.Path = paths.StreamingPaths[i].Paths[0];
                                var dashPath = uriBuilder.ToString();

                                Console.WriteLine("Open the following URL in your browser to play back the file in the Azure Media Player");
                                Console.WriteLine($"https://ampdemo.azureedge.net/?url={dashPath}&aes=true&aestoken=Bearer%3D{token}");
                                Console.WriteLine();
                            }
                        }
                    }
                }
                else if (job.State == JobState.Error)
                {
                    Console.WriteLine($"ERROR: Job finished with error message: {job.Outputs[0].Error.Message}");
                    Console.WriteLine($"ERROR:                   error details: {job.Outputs[0].Error.Details[0].Message}");
                }

                Console.WriteLine("Try Streaming the content using Azure Media Player - https://ampdemo.azureedge.net.");
                Console.WriteLine("Use the Advanced options to see or modify the AES Bearer token in the AMP demo page. When finished press enter to cleanup.");
                Console.Out.Flush();
                Console.ReadLine();

                Console.WriteLine("Cleaning up...");
                Cleanup(client, config.ResourceGroup, config.AccountName, transformName, jobName, outputAsset.Name, input, streamingLocatorName, ContentKeyPolicyName);
            }
            catch (ApiErrorException ex)
            {
                string code    = ex.Body.Error.Code;
                string message = ex.Body.Error.Message;

                Console.WriteLine("ERROR:API call failed with error code: {0} and message: {1}", code, message);
            }
        }
Ejemplo n.º 21
0
        public void JobComboTest()
        {
            using (MockContext context = this.StartMockContextAndInitializeClients(this.GetType()))
            {
                try
                {
                    string transformName        = TestUtilities.GenerateName("transform");
                    string transformDescription = "A test transform";
                    string jobName         = TestUtilities.GenerateName("job");
                    string outputAssetName = TestUtilities.GenerateName("outputAsset");

                    CreateMediaServicesAccount();

                    // Create a transform as it is the parent of Jobs
                    TransformOutput[] outputs = new TransformOutput[]
                    {
                        new TransformOutput(new BuiltInStandardEncoderPreset(EncoderNamedPreset.AdaptiveStreaming))
                    };

                    Transform transform = MediaClient.Transforms.CreateOrUpdate(ResourceGroup, AccountName, transformName, outputs, transformDescription);

                    // List jobs, which should be empty
                    var jobs = MediaClient.Jobs.List(ResourceGroup, AccountName, transformName);
                    Assert.Empty(jobs);

                    // Try to get the job, which should not exist
                    Job job = MediaClient.Jobs.Get(ResourceGroup, AccountName, transformName, jobName);
                    Assert.Null(job);

                    // Create a job using an input from an HTTP url and an output to an Asset
                    Asset outputAsset = MediaClient.Assets.CreateOrUpdate(ResourceGroup, AccountName, outputAssetName, new Asset());

                    JobInputHttp   jobInputHttp = new JobInputHttp(files: new string[] { "https://amssamples.streaming.mediaservices.windows.net/2e91931e-0d29-482b-a42b-9aadc93eb825/AzurePromo.mp4" });
                    JobInputs      jobInputs    = new JobInputs(inputs: new JobInput[] { jobInputHttp });
                    JobOutputAsset jobOutput    = new JobOutputAsset(outputAssetName);
                    JobOutput[]    jobOutputs   = new JobOutput[] { jobOutput };
                    Job            input        = new Job(jobInputs, jobOutputs);

                    Job createdJob = MediaClient.Jobs.Create(ResourceGroup, AccountName, transformName, jobName, input);
                    ValidateJob(createdJob, jobName, null, jobInputs, jobOutputs);

                    // List jobs and validate the created job shows up
                    jobs = MediaClient.Jobs.List(ResourceGroup, AccountName, transformName);
                    Assert.Single(jobs);
                    ValidateJob(jobs.First(), jobName, null, jobInputs, jobOutputs);

                    // Get the newly created job
                    job = MediaClient.Jobs.Get(ResourceGroup, AccountName, transformName, jobName);
                    Assert.NotNull(job);
                    ValidateJob(job, jobName, null, jobInputs, jobOutputs);

                    // If the job isn't already finished, cancel it
                    if (job.State != JobState.Finished)
                    {
                        MediaClient.Jobs.CancelJob(ResourceGroup, AccountName, transformName, jobName);

                        do
                        {
                            System.Threading.Thread.Sleep(15 * 1000);
                            job = MediaClient.Jobs.Get(ResourceGroup, AccountName, transformName, jobName);
                        }while (job.State != JobState.Finished && job.State != JobState.Canceled);
                    }

                    // Delete the job
                    MediaClient.Jobs.Delete(ResourceGroup, AccountName, transformName, jobName);

                    // List jobs, which should be empty again
                    jobs = MediaClient.Jobs.List(ResourceGroup, AccountName, transformName);
                    Assert.Empty(jobs);

                    // Try to get the job, which should not exist
                    job = MediaClient.Jobs.Get(ResourceGroup, AccountName, transformName, jobName);
                    Assert.Null(job);

                    // Delete the transform
                    MediaClient.Transforms.Delete(ResourceGroup, AccountName, transformName);
                }
                finally
                {
                    DeleteMediaServicesAccount();
                }
            }
        }
Ejemplo n.º 22
0
        /// <summary>
        /// Run the sample async.
        /// </summary>
        /// <param name="config">The parm is of type ConfigWrapper. This class reads values from local configuration file.</param>
        /// <returns></returns>
        // <RunAsync>
        private static async Task RunAsync(ConfigWrapper config)
        {
            IAzureMediaServicesClient client = await CreateMediaServicesClientAsync(config);

            // Set the polling interval for long running operations to 2 seconds.
            // The default value is 30 seconds for the .NET client SDK
            client.LongRunningOperationRetryTimeout = 2;

            try
            {
                // Ensure that you have customized encoding Transform.  This is really a one time setup operation.
                Transform adaptiveEncodeTransform = EnsureTransformExists(client, config.ResourceGroup, config.AccountName, transformName);

                // Creating a unique suffix so that we don't have name collisions if you run the sample
                // multiple times without cleaning up.
                string uniqueness      = Guid.NewGuid().ToString().Substring(0, 13);
                string jobName         = "job-" + uniqueness;
                string inputAssetName  = "input-" + uniqueness;
                string outputAssetName = "output-" + uniqueness;

                // The input to the Job is a HTTPS URL pointing to an MP4 file
                var input = new JobInputHttp(
                    baseUri: "https://nimbuscdn-nimbuspm.streaming.mediaservices.windows.net/2b533311-b215-4409-80af-529c3e853622/",
                    files: new List <String> {
                    "Ignite-short.mp4"
                }
                    );

                // Out from the Job must be written to an Asset, so let's create one
                Asset outputAsset = CreateOutputAsset(client, config.ResourceGroup, config.AccountName, outputAssetName);

                Job job = SubmitJob(client, config.ResourceGroup, config.AccountName, transformName, jobName, input, outputAsset.Name);

                DateTime startedTime = DateTime.Now;
                // In this demo code, we will poll for Job status
                // Polling is not a recommended best practice for production applications because of the latency it introduces.
                // Overuse of this API may trigger throttling.
                job = WaitForJobToFinish(client, config.ResourceGroup, config.AccountName, transformName, jobName);

                TimeSpan elapsed = DateTime.Now - startedTime;

                if (job.State == JobState.Finished)
                {
                    Console.WriteLine("Job finished.");
                    if (!Directory.Exists(outputFolder))
                    {
                        Directory.CreateDirectory(outputFolder);
                    }
                    DownloadResults(client, config.ResourceGroup, config.AccountName, outputAsset.Name, outputFolder).Wait();
                }
                else if (job.State == JobState.Error)
                {
                    Console.WriteLine($"ERROR: Job finished with error message: {job.Outputs[0].Error.Message}");
                    Console.WriteLine($"ERROR:                   error details: {job.Outputs[0].Error.Details[0].Message}");
                }
            }
            catch (ApiErrorException ex)
            {
                string code    = ex.Body.Error.Code;
                string message = ex.Body.Error.Message;

                Console.WriteLine("ERROR:API call failed with error code: {0} and message: {1}", code, message);
            }
        }
        /// <summary>
        /// Run the sample async.
        /// </summary>
        /// <param name="config">The parm is of type ConfigWrapper. This class reads values from local configuration file.</param>
        /// <returns></returns>
        // <RunAsync>
        private static async Task RunAsync(ConfigWrapper config)
        {
            IAzureMediaServicesClient client = await CreateMediaServicesClientAsync(config);

            // Set the polling interval for long running operations to 2 seconds.
            // The default value is 30 seconds for the .NET client SDK
            client.LongRunningOperationRetryTimeout = 2;

            try
            {
                // Ensure that you have customized encoding Transform.  This is really a one time setup operation.
                Transform adaptiveEncodeTransform = EnsureTransformExists(client, config.ResourceGroup, config.AccountName, transformName, preset: new BuiltInStandardEncoderPreset(EncoderNamedPreset.AdaptiveStreaming));

                // Creating a unique suffix so that we don't have name collisions if you run the sample
                // multiple times without cleaning up.
                string uniqueness = Guid.NewGuid().ToString().Substring(0, 13);

                string jobName         = "job-" + uniqueness;
                string inputAssetName  = "input-" + uniqueness;
                string outputAssetName = "output-" + uniqueness;


                Asset asset = client.Assets.CreateOrUpdate(config.ResourceGroup, config.AccountName, inputAssetName, new Asset());

                var input = new JobInputHttp(
                    baseUri: "https://nimbuscdn-nimbuspm.streaming.mediaservices.windows.net/2b533311-b215-4409-80af-529c3e853622/",
                    files: new List <String> {
                    "Ignite-short.mp4"
                },
                    label: "input1"
                    );


                Asset outputAsset = CreateOutputAsset(client, config.ResourceGroup, config.AccountName, outputAssetName);

                Job job = SubmitJob(client, config.ResourceGroup, config.AccountName, transformName, jobName, input, outputAsset.Name);

                DateTime startedTime = DateTime.Now;

                job = WaitForJobToFinish(client, config.ResourceGroup, config.AccountName, transformName, jobName);

                TimeSpan elapsed = DateTime.Now - startedTime;

                if (job.State == JobState.Finished)
                {
                    Console.WriteLine("Job finished.");
                    if (!Directory.Exists(outputFolder))
                    {
                        Directory.CreateDirectory(outputFolder);
                    }
                    DownloadResults(client, config.ResourceGroup, config.AccountName, outputAsset.Name, outputFolder).Wait();
                }
                else if (job.State == JobState.Error)
                {
                    Console.WriteLine($"ERROR: Job finished with error message: {job.Outputs[0].Error.Message}");
                    Console.WriteLine($"ERROR:                   error details: {job.Outputs[0].Error.Details[0].Message}");
                }
            }
            catch (ApiErrorException ex)
            {
                string code    = ex.Body.Error.Code;
                string message = ex.Body.Error.Message;

                Console.WriteLine("ERROR:API call failed with error code: {0} and message: {1}", code, message);
            }
        }