├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── deploy-functions.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Encoding ├── .gitattributes ├── .gitignore ├── Encoding.sln ├── Encoding │ ├── .gitignore │ ├── Encoding.csproj │ ├── Helpers │ │ ├── ConfigWrapper.cs │ │ ├── HelpersBasic.cs │ │ └── copyBlobHelpers.cs │ ├── VodFunctions │ │ └── ffmpeg-encoding.cs │ ├── host.json │ └── local.settings.json └── README.md ├── Functions ├── .vscode │ ├── extensions.json │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── Common_Utils │ ├── AssetUtils.cs │ ├── Authentication.cs │ ├── ConfigUtils.cs │ ├── ConfigWrapper.cs │ ├── HttpRequest.cs │ ├── JobUtils.cs │ ├── LiveManifest.cs │ ├── LogUtils.cs │ ├── MediaServicesHelperJsonConverter.cs │ └── TransformUtils.cs ├── CreateEmptyAsset.cs ├── DeleteAsset.cs ├── Functions.csproj ├── Functions.sln ├── Models │ ├── ManifestSegmentData.cs │ └── ManifestTimingData.cs ├── Program.cs ├── PublishAsset.cs ├── README.md ├── SubmitEncodingJob.cs ├── SubmitSubclipJob.cs ├── azuredeploy2.json ├── azuredeploy2.parameter.json ├── azuredeploy2mi.json ├── azuredeploy2mi.parameter.json ├── host.json └── local.settings.json ├── Images ├── DrawingAzureFunctionsNet5.png ├── DrawingAzureFunctionsNet5.vsdx ├── azfunc5appinstance.png ├── azfunc5deploy.png ├── azfunc5deployappsettings.png ├── azfunc5geturl.png ├── azfunc5geturlportal.png ├── azfunc5githubactions.png ├── azfunc5postman.png ├── azfunc5postmandeployed.png ├── azfunc5roleassignment.png └── azfunc5runvscode.png ├── LICENSE.md ├── LiveAndVodDRMOperationsV3 ├── .gitattributes ├── .gitignore ├── .vscode │ ├── extensions.json │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── Images │ └── overview.png ├── LiveAndVodDRMOperationsV3.sln ├── LiveAndVodDRMOperationsV3 │ ├── .gitignore │ ├── Helpers │ │ ├── ConfigWrapper.cs │ │ ├── CosmosHelpers.cs │ │ ├── GenerateAssetInfoHelpers.cs │ │ ├── GenerateInfoHelpers.cs │ │ ├── IrdetoHelpers.cs │ │ ├── ManifestHelpers.cs │ │ ├── MediaServicesHelpers.cs │ │ └── copyBlobHelpers.cs │ ├── LiveAndVodDRMOperationsV3.csproj │ ├── LiveFunctions │ │ ├── check-all-live-event-output.cs │ │ ├── create-clear-streaming-locator.cs │ │ ├── create-live-event-output.cs │ │ ├── delete-live-event-output.cs │ │ ├── delete-streaming-locator.cs │ │ ├── ping.cs │ │ ├── redirector.cs │ │ ├── reset-live-event-output.cs │ │ ├── start-live-event.cs │ │ ├── stop-live-event.cs │ │ └── update-settings.cs │ ├── ManifestTemplate │ │ └── manifest.ism │ ├── Models │ │ ├── BaseModel.cs │ │ ├── GeneralOutputInfo.cs │ │ ├── LiveEventSettingsInfo.cs │ │ ├── StreamingPolicy.cs │ │ ├── VodAssetInfo.cs │ │ ├── VodResource.cs │ │ └── VodSemaphore.cs │ ├── VodFunctions │ │ ├── check-blob-copy-to-asset-status.cs │ │ ├── create-empty-asset.cs │ │ ├── create-vtt-to-asset.cs │ │ ├── generate-ism-manifest.cs │ │ ├── generate-resource.cs │ │ ├── get-asset-info.cs │ │ ├── list-assets-startwith.cs │ │ ├── publish-asset-simple.cs │ │ ├── publish-asset.cs │ │ ├── start-blob-copy-to-asset.cs │ │ └── submit-job.cs │ ├── host.json │ └── local.settings.json ├── LogicApps │ ├── preencodedasset-workflow-deploy.json │ └── semaphore-sample.json └── README.md ├── README.md ├── Tutorial ├── HttpTriggerEncode.cs ├── Program.cs ├── README.md ├── TestFuncVS.csproj └── host.json ├── advanced-vod-workflow ├── .gitignore ├── .vscode │ ├── extensions.json │ ├── launch.json │ ├── settings.json │ └── tasks.json ├── DemoScript.md ├── Functions-documentation.md ├── Functions │ ├── ConvertMesPresetJson.cs │ ├── CreateContentKeyPolicy.cs │ ├── CreateEmptyAsset.cs │ ├── CreateJwtToken.cs │ ├── CreatePlayReadyLicenseTemplate.cs │ ├── CreateStreamingPolicy.cs │ ├── CreateTransform.cs │ ├── CreateWidevineLicenseTemplate.cs │ ├── DeleteAsset.cs │ ├── DeleteContentKeyPolicy.cs │ ├── DeleteStreamingPolicy.cs │ ├── GetAssetUrls.cs │ ├── GetTransform.cs │ ├── MonitorBlobContainerCopyStatus.cs │ ├── MonitorMediaJob.cs │ ├── PublishAsset.cs │ ├── StartBlobContainerCopyToAsset.cs │ ├── SubmitMediaJob.cs │ └── UnpublishAsset.cs ├── README.md ├── SharedLibs │ ├── BlobStorageHelper.cs │ ├── GenericHelper.cs │ ├── MESPresetSchema.cs │ ├── MediaServiceClientCredentials.cs │ ├── MediaServicesConfigWrapper.cs │ ├── MediaServicesHelper.cs │ ├── MediaServicesHelperJsonConverter.cs │ ├── MediaServicesHelperMES.cs │ └── WidevineLicenseTemplate.cs ├── advanced_vod_functions_v3.csproj ├── advanced_vod_functions_v3.sln ├── host.json └── local.settings.json ├── azuredeploy.json ├── azuredeploy.parameters.json ├── logic-app-using-workflow-functions ├── .gitignore ├── README.md ├── publish-logicapp │ ├── Deploy-AzureResourceGroup.ps1 │ ├── Deployment.targets │ ├── LogicApp.json │ ├── LogicApp.parameters.json │ └── publish-logicapp.deployproj ├── publish-streaming.sln ├── upload-and-encode-logicapp │ ├── Deploy-AzureResourceGroup.ps1 │ ├── Deployment.targets │ ├── LogicApp.json │ ├── LogicApp.parameters.json │ └── upload-and-encode-logicapp.deployproj └── upload-and-encode.sln └── sample.env /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | > Please provide us with the following information: 5 | > --------------------------------------------------------------- 6 | 7 | ### This issue is for a: (mark with an `x`) 8 | ``` 9 | - [ ] bug report -> please search issues before submitting 10 | - [ ] feature request 11 | - [ ] documentation issue or request 12 | - [ ] regression (a behavior that used to work and stopped in a new release) 13 | ``` 14 | 15 | ### Minimal steps to reproduce 16 | > 17 | 18 | ### Any log messages given by the failure 19 | > 20 | 21 | ### Expected/desired behavior 22 | > 23 | 24 | ### OS and Version? 25 | > Windows 7, 8 or 10. Linux (which distribution). macOS (Yosemite? El Capitan? Sierra?) 26 | 27 | ### Versions 28 | > 29 | 30 | ### Mention any other details that might be useful 31 | 32 | > --------------------------------------------------------------- 33 | > Thanks! We'll be in touch soon. 34 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | 3 | * ... 4 | 5 | ## Does this introduce a breaking change? 6 | 7 | ``` 8 | [ ] Yes 9 | [ ] No 10 | ``` 11 | 12 | ## Pull Request Type 13 | What kind of change does this Pull Request introduce? 14 | 15 | 16 | ``` 17 | [ ] Bugfix 18 | [ ] Feature 19 | [ ] Code style update (formatting, local variables) 20 | [ ] Refactoring (no functional changes, no api changes) 21 | [ ] Documentation content changes 22 | [ ] Other... Please describe: 23 | ``` 24 | 25 | ## How to Test 26 | * Get the code 27 | 28 | ``` 29 | git clone [repo-address] 30 | cd [repo-name] 31 | git checkout [branch-name] 32 | npm install 33 | ``` 34 | 35 | * Test the code 36 | 37 | ``` 38 | ``` 39 | 40 | ## What to Check 41 | Verify that the following are valid 42 | * ... 43 | 44 | ## Other Information 45 | -------------------------------------------------------------------------------- /.github/workflows/deploy-functions.yml: -------------------------------------------------------------------------------- 1 | name: Build and deploy dotnet 7 app to Azure Function App 2 | 3 | on: 4 | push: 5 | # branches: 6 | # - main 7 | # workflow_dispatch: 8 | 9 | # CONFIGURATION 10 | # For help, go to https://github.com/Azure/Actions 11 | # 12 | # 1. Set up the following secrets in your repository: 13 | # AZURE_FUNCTIONAPP_PUBLISH_PROFILE 14 | # 15 | # 2. Change these variables for your configuration: 16 | env: 17 | AZURE_FUNCTIONAPP_NAME: amsv3functionsxxxxxxxxxxxxx # set this to your application's name 18 | AZURE_FUNCTIONAPP_PACKAGE_PATH: 'Functions' # set this to the path to your web app project, set to '.' to use repository root 19 | DOTNET_VERSION: '7.0.x' # set this to the dotnet version to use 20 | 21 | jobs: 22 | build-and-deploy: 23 | runs-on: windows-latest 24 | environment: dev 25 | steps: 26 | - name: 'Checkout GitHub Action' 27 | uses: actions/checkout@v3 28 | 29 | - name: Setup DotNet ${{ env.DOTNET_VERSION }} Environment 30 | uses: actions/setup-dotnet@v3 31 | with: 32 | dotnet-version: ${{ env.DOTNET_VERSION }} 33 | 34 | - name: 'Resolve Project Dependencies Using Dotnet' 35 | shell: pwsh 36 | run: | 37 | pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}' 38 | dotnet build --configuration Release --p:OutputPath=./output 39 | popd 40 | 41 | - name: 'Run Azure Functions Action' 42 | uses: Azure/functions-action@v1 43 | id: fa 44 | with: 45 | app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }} 46 | package: '${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/output' 47 | publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }} 48 | 49 | # For more samples to get started with GitHub Action workflows to deploy to Azure, refer to https://github.com/Azure/actions-workflow-samples 50 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Media Services v3 Functions for .NET Core Changelog 2 | 3 | 4 | # x.y.z (yyyy-mm-dd) 5 | 6 | *Features* 7 | * ... 8 | 9 | *Bug Fixes* 10 | * ... 11 | 12 | *Breaking Changes* 13 | * ... 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Media Services v3 Functions for .NET Core 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 4 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 5 | the rights to use your contribution. For details, visit https://cla.microsoft.com. 6 | 7 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide 8 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions 9 | provided by the bot. You will only need to do this once across all repos using our CLA. 10 | 11 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 12 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 13 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 14 | 15 | - [Code of Conduct](#coc) 16 | - [Issues and Bugs](#issue) 17 | - [Feature Requests](#feature) 18 | - [Submission Guidelines](#submit) 19 | 20 | ## Code of Conduct 21 | Help us keep this project open and inclusive. Please read and follow our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 22 | 23 | ## Found an Issue? 24 | If you find a bug in the source code or a mistake in the documentation, you can help us by 25 | [submitting an issue](#submit-issue) to the GitHub Repository. Even better, you can 26 | [submit a Pull Request](#submit-pr) with a fix. 27 | 28 | ## Want a Feature? 29 | You can *request* a new feature by [submitting an issue](#submit-issue) to the GitHub 30 | Repository. If you would like to *implement* a new feature, please submit an issue with 31 | a proposal for your work first, to be sure that we can use it. 32 | 33 | * **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr). 34 | 35 | ## Submission Guidelines 36 | 37 | ### Submitting an Issue 38 | Before you submit an issue, search the archive, maybe your question was already answered. 39 | 40 | If your issue appears to be a bug, and hasn't been reported, open a new issue. 41 | Help us to maximize the effort we can spend fixing issues and adding new 42 | features, by not reporting duplicate issues. Providing the following information will increase the 43 | chances of your issue being dealt with quickly: 44 | 45 | * **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps 46 | * **Version** - what version is affected (e.g. 0.1.2) 47 | * **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you 48 | * **Browsers and Operating System** - is this a problem with all browsers? 49 | * **Reproduce the Error** - provide a live example or a unambiguous set of steps 50 | * **Related Issues** - has a similar issue been reported before? 51 | * **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be 52 | causing the problem (line of code or commit) 53 | 54 | You can file new issues by providing the above information at the corresponding repository's issues link: https://github.com/[organization-name]/[repository-name]/issues/new]. 55 | 56 | ### Submitting a Pull Request (PR) 57 | Before you submit your Pull Request (PR) consider the following guidelines: 58 | 59 | * Search the repository (https://github.com/[organization-name]/[repository-name]/pulls) for an open or closed PR 60 | that relates to your submission. You don't want to duplicate effort. 61 | 62 | * Make your changes in a new git fork: 63 | 64 | * Commit your changes using a descriptive commit message 65 | * Push your fork to GitHub: 66 | * In GitHub, create a pull request 67 | * If we suggest changes then: 68 | * Make the required updates. 69 | * Rebase your fork and force push to your GitHub repository (this will update your Pull Request): 70 | 71 | ```shell 72 | git rebase master -i 73 | git push -f 74 | ``` 75 | 76 | That's it! Thank you for your contribution! 77 | -------------------------------------------------------------------------------- /Encoding/.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /Encoding/Encoding.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2035 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Encoding", "Encoding\Encoding.csproj", "{3C581F91-6B42-4B8F-BD24-63BCF6E38088}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {3C581F91-6B42-4B8F-BD24-63BCF6E38088}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {3C581F91-6B42-4B8F-BD24-63BCF6E38088}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {3C581F91-6B42-4B8F-BD24-63BCF6E38088}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {3C581F91-6B42-4B8F-BD24-63BCF6E38088}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {9DD4FB65-B89B-466F-82C7-F83CE3BF1085} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Encoding/Encoding/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | local.settings.json 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary however generally it will be regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | paket-files/ 251 | 252 | # FAKE - F# Make 253 | .fake/ 254 | 255 | # JetBrains Rider 256 | .idea/ 257 | *.sln.iml 258 | 259 | # CodeRush 260 | .cr/ 261 | 262 | # Python Tools for Visual Studio (PTVS) 263 | __pycache__/ 264 | *.pyc -------------------------------------------------------------------------------- /Encoding/Encoding/Encoding.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netcoreapp3.1 4 | v3 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | PreserveNewest 13 | 14 | 15 | PreserveNewest 16 | Never 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | PreserveNewest 30 | 31 | 32 | -------------------------------------------------------------------------------- /Encoding/Encoding/Helpers/ConfigWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.Configuration; 3 | 4 | namespace Encoding.Helpers 5 | { 6 | public class ConfigWrapper 7 | { 8 | private readonly IConfiguration _config; 9 | 10 | public ConfigWrapper(IConfiguration config, string azureRegion = null) 11 | { 12 | _config = config; 13 | AzureRegionCode = azureRegion ?? _config["AzureRegion"]; 14 | } 15 | 16 | public string SubscriptionId => _config["SubscriptionId"]; 17 | 18 | public string ResourceGroupFinalName => _config["ResourceGroupFinalName"]; 19 | 20 | public string ResourceGroup => _config["ResourceGroup"] + (string.IsNullOrEmpty(ResourceGroupFinalName) ? AzureRegionCode : ""); 21 | 22 | public string AccountName => _config["AccountName"] + AzureRegionCode; 23 | 24 | public string AadTenantId => _config["AadTenantId"]; 25 | 26 | public string AadClientId => _config["AadClientId"]; 27 | 28 | public string AadSecret => _config["AadSecret"]; 29 | 30 | public Uri ArmAadAudience => _config["ArmAadAudience"] != null ? new Uri(_config["ArmAadAudience"]) : null; 31 | 32 | public Uri AadEndpoint => _config["AadEndpoint"] != null ? new Uri(_config["AadEndpoint"]) : null; 33 | 34 | public Uri ArmEndpoint => _config["ArmEndpoint"] != null ? new Uri(_config["ArmEndpoint"]) : null; 35 | 36 | public string Region 37 | { 38 | get 39 | { 40 | if (AzureRegionCode == null) 41 | return _config["Region"]; 42 | switch (AzureRegionCode) // codes as defined in AMS Streaming Endpoint hostname - to be completed 43 | { 44 | case "euno": 45 | case "no": 46 | return "North Europe"; 47 | 48 | case "euwe": 49 | case "we": 50 | return "West Europe"; 51 | 52 | default: 53 | return _config["Region"]; 54 | } 55 | } 56 | } 57 | 58 | public string AzureRegionCode { get; } 59 | 60 | public string LiveIngestAccessToken => _config["LiveIngestAccessToken"]; 61 | 62 | public string IrdetoUserName => _config["IrdetoUserName"]; 63 | 64 | public string IrdetoPassword => _config["IrdetoPassword"]; 65 | 66 | public string IrdetoAccountId => _config["IrdetoAccountId"]; 67 | 68 | public string IrdetoSoapService => _config["IrdetoSoapService"]; 69 | 70 | public string IrdetoPlayReadyLAURL => _config["IrdetoPlayReadyLAURL"]; 71 | 72 | public string IrdetoWidevineLAURL => _config["IrdetoWidevineLAURL"]; 73 | 74 | public string IrdetoFairPlayLAURL => _config["IrdetoFairPlayLAURL"]; 75 | 76 | public string IrdetoFairPlayCertificateUrl => _config["IrdetoFairPlayCertificateUrl"]; 77 | 78 | } 79 | } -------------------------------------------------------------------------------- /Encoding/Encoding/Helpers/HelpersBasic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.Extensions.Logging; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace Encoding.Helpers 8 | { 9 | internal class HelpersBasic 10 | { 11 | public static IActionResult ReturnErrorException(ILogger log, Exception ex, string prefixMessage = null) 12 | { 13 | var message = ""; 14 | 15 | return ReturnErrorException(log, 16 | (prefixMessage == null ? string.Empty : prefixMessage + " : ") + ex.Message + message); 17 | } 18 | 19 | public static IActionResult ReturnErrorException(ILogger log, string message, string region = null) 20 | { 21 | LogError(log, message, region); 22 | return new BadRequestObjectResult( 23 | new JObject 24 | { 25 | {"success", false}, 26 | {"errorMessage", message}, 27 | { 28 | "operationsVersion", 29 | AssemblyName.GetAssemblyName(Assembly.GetExecutingAssembly().Location).Version.ToString() 30 | } 31 | }.ToString()); 32 | } 33 | 34 | public static void LogError(ILogger log, string message, string azureRegion = null) 35 | { 36 | log.LogError((azureRegion != null ? "[" + azureRegion + "] " : "") + message); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /Encoding/Encoding/Helpers/copyBlobHelpers.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v2 - Functions 3 | // 4 | // Shared Library 5 | // 6 | 7 | using System; 8 | using System.Threading; 9 | using System.IO; 10 | using Microsoft.WindowsAzure.Storage; 11 | using Microsoft.WindowsAzure.Storage.Blob; 12 | using Microsoft.WindowsAzure.Storage.Auth; 13 | using Microsoft.Azure.WebJobs.Host; 14 | using System.Threading.Tasks; 15 | using System.Collections.Generic; 16 | using Microsoft.Extensions.Logging; 17 | 18 | namespace Encoding.Helpers 19 | { 20 | public class CopyBlobHelpers 21 | { 22 | 23 | static public async void CopyBlobAsync(CloudBlob sourceBlob, CloudBlob destinationBlob) 24 | { 25 | var signature = sourceBlob.GetSharedAccessSignature(new SharedAccessBlobPolicy 26 | { 27 | Permissions = SharedAccessBlobPermissions.Read, 28 | SharedAccessExpiryTime = DateTime.UtcNow.AddHours(24) 29 | }); 30 | await destinationBlob.StartCopyAsync(new Uri(sourceBlob.Uri.AbsoluteUri + signature)); 31 | } 32 | 33 | static public CloudBlobContainer GetCloudBlobContainer(string storageAccountName, string storageAccountKey, string containerName) 34 | { 35 | 36 | CloudStorageAccount sourceStorageAccount = new CloudStorageAccount(new StorageCredentials(storageAccountName, storageAccountKey), true); 37 | 38 | CloudBlobClient sourceCloudBlobClient = sourceStorageAccount.CreateCloudBlobClient(); 39 | 40 | return sourceCloudBlobClient.GetContainerReference(containerName); 41 | 42 | } 43 | 44 | } 45 | 46 | } 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Encoding/Encoding/host.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /Encoding/Encoding/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "AzureWebJobsStorage": "UseDevelopmentStorage=true", 5 | "FUNCTIONS_WORKER_RUNTIME": "dotnet" 6 | } 7 | } -------------------------------------------------------------------------------- /Encoding/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | services: media-services,functions 3 | platforms: dotnetcore 4 | author: xpouyat 5 | --- 6 | 7 | # Ffmpeg encoding with an Azure function 8 | 9 | This Visual Studio 2019 Solution exposes an Azure Function that encodes an Azure Storage blob with ffmpeg. [Azure Functions Premium plan](https://docs.microsoft.com/en-us/azure/azure-functions/functions-premium-plan 10 | ) is recommended. 11 | 12 | This Functions code is based on Azure Functions v3. 13 | 14 | ## Fork and download a copy 15 | 16 | If not already done : fork the repo, download a local copy. 17 | 18 | ## Ffmpeg 19 | 20 | Download ffmpeg from the Internet and copy ffmpeg.exe in \Encoding\Encoding\ffmpeg folder. 21 | In Visual Studio, open the file properties, "Build action" should be "Content", with "Copy to Output Direcotry" set to "Copy if newer". 22 | 23 | ## Publish the function to Azure 24 | 25 | Open the solution with Visual Studio and publish the functions to Azure. 26 | It is recommended to use a **premium plan** to avoid functions timeout (Premium gives you 30 min and a more powerfull host). 27 | It is possible to [unbound run duration](https://docs.microsoft.com/en-us/azure/azure-functions/functions-premium-plan#longer-run-duration). 28 | 29 | JSON input body of the function : 30 | 31 | ```json 32 | { 33 | "sasInputUrl":"https://mysasurlofthesourceblob", 34 | "sasOutputUrl":"https://mysasurlofthedestinationblob", 35 | "ffmpegArguments" : " -i {input} {output} -y" 36 | } 37 | ``` 38 | -------------------------------------------------------------------------------- /Functions/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-azuretools.vscode-azurefunctions", 4 | "ms-dotnettools.csharp" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /Functions/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Attach to .NET Functions", 6 | "type": "coreclr", 7 | "request": "attach", 8 | "processId": "${command:azureFunctions.pickProcess}", 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /Functions/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "azureFunctions.deploySubpath": "bin/Release/net7.0/publish", 3 | "azureFunctions.projectLanguage": "C#", 4 | "azureFunctions.projectRuntime": "~4", 5 | "debug.internalConsoleOptions": "neverOpen", 6 | "azureFunctions.preDeployTask": "publish (functions)" 7 | } -------------------------------------------------------------------------------- /Functions/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "clean (functions)", 6 | "command": "dotnet", 7 | "args": [ 8 | "clean", 9 | "/property:GenerateFullPaths=true", 10 | "/consoleloggerparameters:NoSummary" 11 | ], 12 | "type": "process", 13 | "problemMatcher": "$msCompile" 14 | }, 15 | { 16 | "label": "build (functions)", 17 | "command": "dotnet", 18 | "args": [ 19 | "build", 20 | "/property:GenerateFullPaths=true", 21 | "/consoleloggerparameters:NoSummary" 22 | ], 23 | "type": "process", 24 | "dependsOn": "clean (functions)", 25 | "group": { 26 | "kind": "build", 27 | "isDefault": true 28 | }, 29 | "problemMatcher": "$msCompile" 30 | }, 31 | { 32 | "label": "clean release (functions)", 33 | "command": "dotnet", 34 | "args": [ 35 | "clean", 36 | "--configuration", 37 | "Release", 38 | "/property:GenerateFullPaths=true", 39 | "/consoleloggerparameters:NoSummary" 40 | ], 41 | "type": "process", 42 | "problemMatcher": "$msCompile" 43 | }, 44 | { 45 | "label": "publish (functions)", 46 | "command": "dotnet", 47 | "args": [ 48 | "publish", 49 | "--configuration", 50 | "Release", 51 | "/property:GenerateFullPaths=true", 52 | "/consoleloggerparameters:NoSummary" 53 | ], 54 | "type": "process", 55 | "dependsOn": "clean release (functions)", 56 | "problemMatcher": "$msCompile" 57 | }, 58 | { 59 | "type": "func", 60 | "dependsOn": "build (functions)", 61 | "options": { 62 | "cwd": "${workspaceFolder}/bin/Debug/net7.0" 63 | }, 64 | "command": "host start", 65 | "isBackground": true, 66 | "problemMatcher": "$func-dotnet-watch" 67 | } 68 | ] 69 | } -------------------------------------------------------------------------------- /Functions/Common_Utils/AssetUtils.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using Microsoft.Azure.Management.Media; 5 | using Microsoft.Azure.Management.Media.Models; 6 | using Microsoft.Extensions.Logging; 7 | using System.Threading.Tasks; 8 | 9 | namespace Common_Utils 10 | { 11 | public class AssetUtils 12 | { 13 | /// 14 | /// Creates an asset. 15 | /// 16 | /// The Media Services client. 17 | /// The name of the resource group within the Azure subscription. 18 | /// The Media Services account name. 19 | /// The asset name. 20 | /// The asset storage name. 21 | /// 22 | public static async Task CreateAssetAsync(IAzureMediaServicesClient client, ILogger log, string resourceGroupName, string accountName, string assetName, string storageAccountName = null, string description = null) 23 | { 24 | Asset asset; 25 | try 26 | { 27 | // Check if an Asset already exists 28 | asset = await client.Assets.GetAsync(resourceGroupName, accountName, assetName); 29 | 30 | // The asset already exists and we are going to overwrite it. In your application, if you don't want to overwrite 31 | // an existing asset, use an unique name. 32 | log.LogInformation($"Warning: The asset named {assetName} already exists. It will be overwritten by the function."); 33 | 34 | } 35 | catch (ErrorResponseException ex) when (ex.Response.StatusCode == System.Net.HttpStatusCode.NotFound) 36 | { 37 | log.LogInformation("Creating an asset..."); 38 | asset = new Asset(storageAccountName: storageAccountName); 39 | } 40 | 41 | if (description != null) 42 | { 43 | asset.Description = description; 44 | } 45 | 46 | return await client.Assets.CreateOrUpdateAsync(resourceGroupName, accountName, assetName, asset); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /Functions/Common_Utils/Authentication.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using Azure.Core; 5 | using Azure.Identity; 6 | using Microsoft.Azure.Management.Media; 7 | using Microsoft.Rest; 8 | using System; 9 | using System.Threading.Tasks; 10 | 11 | namespace Common_Utils 12 | { 13 | public class Authentication 14 | { 15 | public static readonly string TokenType = "Bearer"; 16 | 17 | private static readonly Lazy _msiCredential = new(() => 18 | { 19 | // https://docs.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet 20 | // Using DefaultAzureCredential allows for local dev by setting environment variables for the current user, provided said user 21 | // has the necessary credentials to perform the operations the MSI of the Function app needs in order to do its work. Including 22 | // interactive credentials will allow browser-based login when developing locally. 23 | 24 | return new DefaultAzureCredential(includeInteractiveCredentials: true); 25 | }); 26 | 27 | /// 28 | /// Creates the AzureMediaServicesClient object based on the credentials 29 | /// supplied in local configuration file or from other types of authentication. 30 | /// 31 | /// The param is of type ConfigWrapper, which reads values from local configuration file. 32 | /// A task. 33 | // 34 | public static async Task CreateMediaServicesClientAsync(ConfigWrapper config) 35 | { 36 | string[] scopes = new[] { config.ArmAadAudience + "/.default" }; 37 | TokenCredential tokenCred = _msiCredential.Value; 38 | AccessToken accesToken = await tokenCred.GetTokenAsync(new TokenRequestContext(scopes), new System.Threading.CancellationToken()); 39 | ServiceClientCredentials credentials = new TokenCredentials(accesToken.Token, TokenType); 40 | return new AzureMediaServicesClient(config.ArmEndpoint, credentials) 41 | { 42 | SubscriptionId = config.SubscriptionId 43 | }; 44 | } 45 | // 46 | } 47 | } -------------------------------------------------------------------------------- /Functions/Common_Utils/ConfigUtils.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using Microsoft.Extensions.Configuration; 5 | using System; 6 | using System.IO; 7 | 8 | namespace Common_Utils 9 | { 10 | /// 11 | /// This class is used to load the configuration and settings 12 | /// 13 | public static class ConfigUtils 14 | { 15 | public static ConfigWrapper GetConfig() 16 | { 17 | // If Visual Studio is used, let's read the .env file which should be in the root folder (same folder than the solution .sln file). 18 | // Same code will work in VS Code, but VS Code uses also launch.json to get the .env file. 19 | // You can create this ".env" file by saving the "sample.env" file as ".env" file and fill it with the right values. 20 | try 21 | { 22 | Load(".env"); 23 | } 24 | catch 25 | { 26 | 27 | } 28 | 29 | ConfigWrapper config = new(new ConfigurationBuilder() 30 | .SetBasePath(Directory.GetCurrentDirectory()) 31 | .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) 32 | .AddEnvironmentVariables() // parses the values from the optional .env file at the solution root 33 | .Build()); 34 | 35 | // For Azure.Identity environment credential 36 | // see https://docs.microsoft.com/en-us/dotnet/api/azure.identity.environmentcredential?view=azure-dotnet 37 | Environment.SetEnvironmentVariable("AZURE_TENANT_ID", config.AadTenantId); 38 | Environment.SetEnvironmentVariable("AZURE_CLIENT_ID", config.AadClientId); 39 | Environment.SetEnvironmentVariable("AZURE_CLIENT_SECRET", config.AadSecret); 40 | 41 | return config; 42 | } 43 | 44 | 45 | /// 46 | /// Loads the .env file and stores the values as variables 47 | /// 48 | /// 49 | private static void Load(string envFileName) 50 | { 51 | // let's find the root folder where the .env file can be found 52 | var rootPath = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase))))); 53 | var filePath = Path.Combine(rootPath, envFileName); 54 | // let's remove file:// 55 | filePath = new Uri(filePath).LocalPath; 56 | 57 | if (!File.Exists(filePath)) 58 | return; 59 | 60 | foreach (var line in File.ReadAllLines(filePath)) 61 | { 62 | if (line.StartsWith("#")) 63 | { 64 | // It's a comment 65 | continue; 66 | } 67 | 68 | var parts = line.Split( 69 | '=', 70 | 2, 71 | StringSplitOptions.RemoveEmptyEntries); 72 | 73 | if (parts.Length != 2) 74 | continue; 75 | 76 | var p0 = parts[0].Trim(); 77 | var p1 = parts[1].Trim(); 78 | 79 | if (p1.StartsWith("\"")) 80 | { 81 | p1 = p1[1..^1]; 82 | } 83 | 84 | Environment.SetEnvironmentVariable(p0, p1); 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Functions/Common_Utils/ConfigWrapper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using Microsoft.Extensions.Configuration; 5 | using System; 6 | 7 | namespace Common_Utils 8 | { 9 | /// 10 | /// This class reads values from local configuration file resources/conf/appsettings.json. 11 | /// Please change the configuration using your account information. For more information, see 12 | /// https://docs.microsoft.com/azure/media-services/latest/access-api-cli-how-to. For security 13 | /// reasons, do not check in the configuration file to source control. 14 | /// 15 | public class ConfigWrapper 16 | { 17 | private readonly IConfiguration _config; 18 | 19 | public ConfigWrapper(IConfiguration config) 20 | { 21 | _config = config; 22 | } 23 | 24 | public string SubscriptionId 25 | { 26 | get { return _config["AZURE_SUBSCRIPTION_ID"]; } 27 | } 28 | 29 | public string ResourceGroup 30 | { 31 | get { return _config["AZURE_RESOURCE_GROUP"]; } 32 | } 33 | 34 | public string AccountName 35 | { 36 | get { return _config["AZURE_MEDIA_SERVICES_ACCOUNT_NAME"]; } 37 | } 38 | 39 | public string AadTenantId 40 | { 41 | get { return _config["AZURE_TENANT_ID"]; } 42 | } 43 | 44 | public string AadClientId 45 | { 46 | get { return _config["AZURE_CLIENT_ID"]; } 47 | } 48 | 49 | public string AadSecret 50 | { 51 | get { return _config["AZURE_CLIENT_SECRET"]; } 52 | } 53 | 54 | public Uri ArmAadAudience 55 | { 56 | get { return new Uri(_config["AZURE_ARM_TOKEN_AUDIENCE"]); } 57 | } 58 | 59 | public Uri AadEndpoint 60 | { 61 | get { return new Uri(_config["AZURE_AAD_ENDPOINT"]); } 62 | } 63 | 64 | public Uri ArmEndpoint 65 | { 66 | get { return new Uri(_config["AZURE_ARM_ENDPOINT"]); } 67 | } 68 | } 69 | } 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /Functions/Common_Utils/HttpRequest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.Functions.Worker.Http; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | using System.Net; 5 | 6 | namespace Common_Utils 7 | { 8 | class HttpRequest 9 | { 10 | private static readonly JsonSerializerSettings SerializerSettings = new() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.Indented }; 11 | 12 | /// 13 | /// Generates the response with HttpStatusCode.OK and JSON body 14 | /// 15 | /// HttpRequestData object 16 | /// Object to serialize 17 | /// 18 | public static HttpResponseData ResponseOk(HttpRequestData req, object data, HttpStatusCode statuscode = HttpStatusCode.OK) 19 | { 20 | var response = req.CreateResponse(statuscode); 21 | response.Headers.Add("Content-Type", "application/json"); 22 | if (data != null) 23 | { 24 | response.WriteString(JsonConvert.SerializeObject(data, SerializerSettings)); 25 | } 26 | return response; 27 | } 28 | 29 | /// 30 | /// Generates the response with HttpStatusCode.BadRequest and JSON body 31 | /// 32 | /// HttpRequestData object 33 | /// Error message 34 | /// 35 | public static HttpResponseData ResponseBadRequest(HttpRequestData req, string message) 36 | { 37 | dynamic dataNotOk = new JObject(); 38 | dataNotOk.errorMessage = message; 39 | 40 | var response = req.CreateResponse(HttpStatusCode.BadRequest); 41 | response.Headers.Add("Content-Type", "application/json"); 42 | var stringJson = JsonConvert.SerializeObject(dataNotOk, SerializerSettings); 43 | response.WriteString((string)stringJson); 44 | return response; 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /Functions/Common_Utils/JobUtils.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using Microsoft.Azure.Management.Media; 5 | using Microsoft.Azure.Management.Media.Models; 6 | using Microsoft.Extensions.Logging; 7 | using System.Threading.Tasks; 8 | 9 | namespace Common_Utils 10 | { 11 | public class JobUtils 12 | { 13 | /// 14 | /// Submits a request to Media Services to apply the specified Transform to a given input video. 15 | /// 16 | /// The Media Services client. 17 | /// Function logger. 18 | /// The name of the resource group within the Azure subscription. 19 | /// The Media Services account name. 20 | /// The name of the transform. 21 | /// The (unique) name of the job. 22 | /// The input of the job 23 | /// The (unique) name of the output asset that will store the result of the encoding job. 24 | public static async Task SubmitJobAsync( 25 | IAzureMediaServicesClient client, 26 | ILogger log, 27 | string resourceGroupName, 28 | string accountName, 29 | string transformName, 30 | string jobName, 31 | JobInput jobInput, 32 | string outputAssetName 33 | ) 34 | { 35 | JobOutput[] jobOutputs = 36 | { 37 | new JobOutputAsset(outputAssetName), 38 | }; 39 | 40 | // In this example, we are assuming that the job name is unique. 41 | // 42 | // If you already have a job with the desired name, use the Jobs.Get method 43 | // to get the existing job. In Media Services v3, Get methods on entities returns null 44 | // if the entity doesn't exist (a case-insensitive check on the name). 45 | Job job; 46 | try 47 | { 48 | log.LogInformation("Creating a job..."); 49 | job = await client.Jobs.CreateAsync( 50 | resourceGroupName, 51 | accountName, 52 | transformName, 53 | jobName, 54 | new Job 55 | { 56 | Input = jobInput, 57 | Outputs = jobOutputs, 58 | }); 59 | 60 | } 61 | catch (ErrorResponseException ex) 62 | { 63 | log.LogError( 64 | $"ERROR: API call failed with error code '{ex.Body.Error.Code}' and message '{ex.Body.Error.Message}'."); 65 | 66 | throw; 67 | } 68 | 69 | return job; 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /Functions/Common_Utils/LogUtils.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | 4 | namespace Common_Utils 5 | { 6 | class LogUtils 7 | { 8 | public static string LogError(ILogger log, Exception ex, string err) 9 | { 10 | log.LogError(err); 11 | log.LogError($"{ex.Message}"); 12 | return err.TrimEnd() + " " + ex.Message; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Functions/DeleteAsset.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.IO; 6 | using System.Text.Json; 7 | using System.Threading.Tasks; 8 | using Common_Utils; 9 | using Microsoft.Azure.Functions.Worker; 10 | using Microsoft.Azure.Functions.Worker.Http; 11 | using Microsoft.Azure.Management.Media; 12 | using Microsoft.Azure.Management.Media.Models; 13 | using Microsoft.Extensions.Logging; 14 | using Newtonsoft.Json; 15 | 16 | namespace Functions 17 | { 18 | public static class DeleteAsset 19 | { 20 | /// 21 | /// Data to pass as an input to the function 22 | /// 23 | private class RequestBodyModel 24 | { 25 | /// 26 | /// Name of the asset to delete. 27 | /// Mandatory. 28 | /// 29 | [JsonProperty("assetName")] 30 | public string AssetName { get; set; } 31 | } 32 | 33 | /// 34 | /// Function which deletes an asset. 35 | /// 36 | /// 37 | /// 38 | /// 39 | [Function("DeleteAsset")] 40 | public static async Task Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req, FunctionContext executionContext) 41 | { 42 | var log = executionContext.GetLogger("DeleteAsset"); 43 | log.LogInformation("C# HTTP trigger function processed a request."); 44 | 45 | // Get request body data. 46 | string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); 47 | var data = (RequestBodyModel)JsonConvert.DeserializeObject(requestBody, typeof(RequestBodyModel)); 48 | 49 | // Return bad request if asset name is not passed in 50 | if (data.AssetName == null) 51 | { 52 | return HttpRequest.ResponseBadRequest(req, "Please pass asset name in the request body"); 53 | } 54 | 55 | ConfigWrapper config = ConfigUtils.GetConfig(); 56 | 57 | IAzureMediaServicesClient client; 58 | try 59 | { 60 | client = await Authentication.CreateMediaServicesClientAsync(config); 61 | log.LogInformation("AMS Client created."); 62 | } 63 | catch (Exception e) 64 | { 65 | if (e.Source.Contains("ActiveDirectory")) 66 | { 67 | log.LogError("TIP: Make sure that you have filled out the appsettings.json file before running this sample."); 68 | } 69 | log.LogError($"{e.Message}"); 70 | 71 | return HttpRequest.ResponseBadRequest(req, e.Message); 72 | } 73 | 74 | // Set the polling interval for long running operations to 2 seconds. 75 | // The default value is 30 seconds for the .NET client SDK 76 | client.LongRunningOperationRetryTimeout = 2; 77 | 78 | try 79 | { 80 | // let's delete the asset 81 | await client.Assets.DeleteAsync(config.ResourceGroup, config.AccountName, data.AssetName); 82 | log.LogInformation($"Asset '{data.AssetName}' deleted."); 83 | } 84 | catch (ErrorResponseException ex) 85 | { 86 | return HttpRequest.ResponseBadRequest(req, LogUtils.LogError(log, ex, "Error when deleting the asset.")); 87 | } 88 | 89 | return HttpRequest.ResponseOk(req, null); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Functions/Functions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net7.0 4 | v4 5 | Exe 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | PreserveNewest 20 | 21 | 22 | PreserveNewest 23 | Never 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Functions/Functions.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31424.327 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Functions", "Functions.csproj", "{DAEB4FF5-CA3E-4106-8060-F6999174B92E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {DAEB4FF5-CA3E-4106-8060-F6999174B92E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {DAEB4FF5-CA3E-4106-8060-F6999174B92E}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {DAEB4FF5-CA3E-4106-8060-F6999174B92E}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {DAEB4FF5-CA3E-4106-8060-F6999174B92E}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {CC03063C-062A-490A-9868-F61EBD404F44} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Functions/Models/ManifestSegmentData.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | namespace Common_Utils 5 | { 6 | public class ManifestSegmentData 7 | { 8 | public ulong timestamp; 9 | public bool calculated; // it means the timestamp has been calculated from previous 10 | public bool timestamp_mismatch; // if there is a mismatch 11 | } 12 | } -------------------------------------------------------------------------------- /Functions/Models/ManifestTimingData.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace Common_Utils 8 | { 9 | public class ManifestTimingData 10 | { 11 | public TimeSpan AssetDuration { get; set; } 12 | public ulong TimestampOffset { get; set; } 13 | public long? TimeScale { get; set; } 14 | public bool IsLive { get; set; } 15 | public bool Error { get; set; } 16 | public List TimestampList { get; set; } 17 | public ulong TimestampEndLastChunk { get; set; } 18 | public bool DiscontinuityDetected { get; set; } 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /Functions/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using Microsoft.Extensions.Hosting; 5 | 6 | namespace Functions 7 | { 8 | public class Program 9 | { 10 | public static void Main() 11 | { 12 | var host = new HostBuilder() 13 | .ConfigureFunctionsWorkerDefaults() 14 | .Build(); 15 | 16 | host.Run(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Functions/azuredeploy2.parameter.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "appName": { 6 | "value": "GEN-UNIQUE" 7 | }, 8 | "functionKey": { 9 | "value" : "GEN-UNIQUE" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Functions/azuredeploy2mi.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "appName": { 6 | "type": "string", 7 | "defaultValue": "[concat('amsv3functions', uniqueString(resourceGroup().id))]", 8 | "metadata": { 9 | "description": "The name of the Function application to be deployed. The name will automatically be made unique on deployment, all lowercase letters or numbers with no spaces." 10 | } 11 | }, 12 | "mediaServicesAccountSubscriptionId": { 13 | "type": "string", 14 | "defaultValue": "[subscription().subscriptionId]", 15 | "metadata": { 16 | "description": "Subscription Id for your Media Services account." 17 | } 18 | }, 19 | "mediaServicesAccountResourceGroup": { 20 | "type": "string", 21 | "defaultValue": "", 22 | "metadata": { 23 | "description": "Resource Group Name of your Media Services account." 24 | } 25 | }, 26 | "mediaServicesAccountName": { 27 | "type": "string", 28 | "defaultValue": "", 29 | "metadata": { 30 | "description": "Your Media Services account name." 31 | } 32 | }, 33 | "mediaServicesAccountAzureActiveDirectoryTenantId": { 34 | "type": "string", 35 | "defaultValue": "", 36 | "metadata": { 37 | "description": "Azure Active Directory tenant Id or domain of your Media Services account." 38 | } 39 | } 40 | }, 41 | "variables": { 42 | "functionAppName": "[parameters('appName')]", 43 | "storageAccountName": "[concat(uniquestring(resourceGroup().id), 'azfunctions')]", 44 | "hostingPlanName": "[parameters('appName')]" 45 | }, 46 | "resources": [ 47 | { 48 | "type": "Microsoft.Storage/storageAccounts", 49 | "name": "[variables('storageAccountName')]", 50 | "apiVersion": "2021-04-01", 51 | "location": "[resourceGroup().location]", 52 | "comments": "This storage account is used by the functions.", 53 | "sku": { 54 | "name": "Standard_LRS" 55 | }, 56 | "kind": "Storage" 57 | }, 58 | { 59 | "type": "Microsoft.Web/serverfarms", 60 | "name": "[variables('hostingPlanName')]", 61 | "apiVersion": "2020-12-01", 62 | "location": "[resourceGroup().location]", 63 | "comments": "This hosting plan is created to deploy the function app and set the billing sku tier", 64 | "sku": { 65 | "name": "Y1", 66 | "tier": "Dynamic" 67 | }, 68 | "properties": { 69 | "name": "[variables('hostingPlanName')]", 70 | "computeMode": "Dynamic" 71 | } 72 | }, 73 | { 74 | "type": "Microsoft.Web/sites", 75 | "name": "[variables('functionAppName')]", 76 | "apiVersion": "2020-12-01", 77 | "location": "[resourceGroup().location]", 78 | "identity": { 79 | "type": "SystemAssigned" 80 | }, 81 | "comments": "This function app depends on the Media Services account and storage account", 82 | "dependsOn": [ 83 | "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]", 84 | "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]" 85 | ], 86 | "kind": "functionapp", 87 | "properties": { 88 | "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]", 89 | "siteConfig": { 90 | "appSettings": [ 91 | { 92 | "name": "AzureWebJobsStorage", 93 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]" 94 | }, 95 | { 96 | "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING", 97 | "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]" 98 | }, 99 | { 100 | "name": "WEBSITE_CONTENTSHARE", 101 | "value": "[toLower(variables('functionAppName'))]" 102 | }, 103 | { 104 | "name": "FUNCTIONS_EXTENSION_VERSION", 105 | "value": "~4" 106 | }, 107 | { 108 | "name": "FUNCTIONS_WORKER_RUNTIME", 109 | "value": "dotnet-isolated" 110 | }, 111 | { 112 | "name": "WEBSITE_RUN_FROM_PACKAGE", 113 | "value": "1" 114 | }, 115 | { 116 | "name": "AZURE_SUBSCRIPTION_ID", 117 | "value": "[parameters('mediaServicesAccountSubscriptionId')]" 118 | }, 119 | { 120 | "name": "AZURE_RESOURCE_GROUP", 121 | "value": "[parameters('mediaServicesAccountResourceGroup')]" 122 | }, 123 | { 124 | "name": "AZURE_MEDIA_SERVICES_ACCOUNT_NAME", 125 | "value": "[parameters('mediaServicesAccountName')]" 126 | }, 127 | { 128 | "name": "AZURE_TENANT_ID", 129 | "value": "[parameters('mediaServicesAccountAzureActiveDirectoryTenantId')]" 130 | }, 131 | { 132 | "name": "AZURE_AAD_ENDPOINT", 133 | "value": "https://login.microsoftonline.com" 134 | }, 135 | { 136 | "name": "AZURE_ARM_ENDPOINT", 137 | "value": "https://management.azure.com/" 138 | }, 139 | { 140 | "name": "AZURE_ARM_TOKEN_AUDIENCE", 141 | "value": "https://management.core.windows.net/" 142 | } 143 | ] 144 | } 145 | } 146 | } 147 | ], 148 | "outputs": {} 149 | } -------------------------------------------------------------------------------- /Functions/azuredeploy2mi.parameter.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "appName": { 6 | "value": "GEN-UNIQUE" 7 | }, 8 | "functionKey": { 9 | "value" : "GEN-UNIQUE" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Functions/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "applicationInsights": { 5 | "samplingSettings": { 6 | "isEnabled": true, 7 | "excludedTypes": "Request" 8 | } 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /Functions/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "AzureWebJobsStorage": "", 5 | "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated" 6 | } 7 | } -------------------------------------------------------------------------------- /Images/DrawingAzureFunctionsNet5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/media-services-v3-dotnet-core-functions-integration/4f0d6a3e0de03777d8ce49b4a7721cc0b6255ccc/Images/DrawingAzureFunctionsNet5.png -------------------------------------------------------------------------------- /Images/DrawingAzureFunctionsNet5.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/media-services-v3-dotnet-core-functions-integration/4f0d6a3e0de03777d8ce49b4a7721cc0b6255ccc/Images/DrawingAzureFunctionsNet5.vsdx -------------------------------------------------------------------------------- /Images/azfunc5appinstance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/media-services-v3-dotnet-core-functions-integration/4f0d6a3e0de03777d8ce49b4a7721cc0b6255ccc/Images/azfunc5appinstance.png -------------------------------------------------------------------------------- /Images/azfunc5deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/media-services-v3-dotnet-core-functions-integration/4f0d6a3e0de03777d8ce49b4a7721cc0b6255ccc/Images/azfunc5deploy.png -------------------------------------------------------------------------------- /Images/azfunc5deployappsettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/media-services-v3-dotnet-core-functions-integration/4f0d6a3e0de03777d8ce49b4a7721cc0b6255ccc/Images/azfunc5deployappsettings.png -------------------------------------------------------------------------------- /Images/azfunc5geturl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/media-services-v3-dotnet-core-functions-integration/4f0d6a3e0de03777d8ce49b4a7721cc0b6255ccc/Images/azfunc5geturl.png -------------------------------------------------------------------------------- /Images/azfunc5geturlportal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/media-services-v3-dotnet-core-functions-integration/4f0d6a3e0de03777d8ce49b4a7721cc0b6255ccc/Images/azfunc5geturlportal.png -------------------------------------------------------------------------------- /Images/azfunc5githubactions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/media-services-v3-dotnet-core-functions-integration/4f0d6a3e0de03777d8ce49b4a7721cc0b6255ccc/Images/azfunc5githubactions.png -------------------------------------------------------------------------------- /Images/azfunc5postman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/media-services-v3-dotnet-core-functions-integration/4f0d6a3e0de03777d8ce49b4a7721cc0b6255ccc/Images/azfunc5postman.png -------------------------------------------------------------------------------- /Images/azfunc5postmandeployed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/media-services-v3-dotnet-core-functions-integration/4f0d6a3e0de03777d8ce49b4a7721cc0b6255ccc/Images/azfunc5postmandeployed.png -------------------------------------------------------------------------------- /Images/azfunc5roleassignment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/media-services-v3-dotnet-core-functions-integration/4f0d6a3e0de03777d8ce49b4a7721cc0b6255ccc/Images/azfunc5roleassignment.png -------------------------------------------------------------------------------- /Images/azfunc5runvscode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/media-services-v3-dotnet-core-functions-integration/4f0d6a3e0de03777d8ce49b4a7721cc0b6255ccc/Images/azfunc5runvscode.png -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-azuretools.vscode-azurefunctions", 4 | "ms-dotnettools.csharp" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Attach to .NET Functions", 6 | "type": "coreclr", 7 | "request": "attach", 8 | "processId": "${command:azureFunctions.pickProcess}" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "azureFunctions.deploySubpath": "LiveAndVodDRMOperationsV3/bin/Release/net5.0/publish", 3 | "azureFunctions.projectLanguage": "C#", 4 | "azureFunctions.projectRuntime": "~3", 5 | "debug.internalConsoleOptions": "neverOpen", 6 | "azureFunctions.preDeployTask": "publish (functions)" 7 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "clean (functions)", 6 | "command": "dotnet", 7 | "args": [ 8 | "clean", 9 | "/property:GenerateFullPaths=true", 10 | "/consoleloggerparameters:NoSummary" 11 | ], 12 | "type": "process", 13 | "problemMatcher": "$msCompile", 14 | "options": { 15 | "cwd": "${workspaceFolder}/LiveAndVodDRMOperationsV3" 16 | } 17 | }, 18 | { 19 | "label": "build (functions)", 20 | "command": "dotnet", 21 | "args": [ 22 | "build", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "type": "process", 27 | "dependsOn": "clean (functions)", 28 | "group": { 29 | "kind": "build", 30 | "isDefault": true 31 | }, 32 | "problemMatcher": "$msCompile", 33 | "options": { 34 | "cwd": "${workspaceFolder}/LiveAndVodDRMOperationsV3" 35 | } 36 | }, 37 | { 38 | "label": "clean release (functions)", 39 | "command": "dotnet", 40 | "args": [ 41 | "clean", 42 | "--configuration", 43 | "Release", 44 | "/property:GenerateFullPaths=true", 45 | "/consoleloggerparameters:NoSummary" 46 | ], 47 | "type": "process", 48 | "problemMatcher": "$msCompile", 49 | "options": { 50 | "cwd": "${workspaceFolder}/LiveAndVodDRMOperationsV3" 51 | } 52 | }, 53 | { 54 | "label": "publish (functions)", 55 | "command": "dotnet", 56 | "args": [ 57 | "publish", 58 | "--configuration", 59 | "Release", 60 | "/property:GenerateFullPaths=true", 61 | "/consoleloggerparameters:NoSummary" 62 | ], 63 | "type": "process", 64 | "dependsOn": "clean release (functions)", 65 | "problemMatcher": "$msCompile", 66 | "options": { 67 | "cwd": "${workspaceFolder}/LiveAndVodDRMOperationsV3" 68 | } 69 | }, 70 | { 71 | "type": "func", 72 | "dependsOn": "build (functions)", 73 | "options": { 74 | "cwd": "${workspaceFolder}/LiveAndVodDRMOperationsV3/bin/Debug/net5.0" 75 | }, 76 | "command": "host start", 77 | "isBackground": true, 78 | "problemMatcher": "$func-dotnet-watch" 79 | } 80 | ] 81 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/Images/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/media-services-v3-dotnet-core-functions-integration/4f0d6a3e0de03777d8ce49b4a7721cc0b6255ccc/LiveAndVodDRMOperationsV3/Images/overview.png -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2035 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LiveAndVodDRMOperationsV3", "LiveAndVodDRMOperationsV3\LiveAndVodDRMOperationsV3.csproj", "{3C581F91-6B42-4B8F-BD24-63BCF6E38088}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {3C581F91-6B42-4B8F-BD24-63BCF6E38088}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {3C581F91-6B42-4B8F-BD24-63BCF6E38088}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {3C581F91-6B42-4B8F-BD24-63BCF6E38088}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {3C581F91-6B42-4B8F-BD24-63BCF6E38088}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {9DD4FB65-B89B-466F-82C7-F83CE3BF1085} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | local.settings.json 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary however generally it will be regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | paket-files/ 251 | 252 | # FAKE - F# Make 253 | .fake/ 254 | 255 | # JetBrains Rider 256 | .idea/ 257 | *.sln.iml 258 | 259 | # CodeRush 260 | .cr/ 261 | 262 | # Python Tools for Visual Studio (PTVS) 263 | __pycache__/ 264 | *.pyc -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/Helpers/ConfigWrapper.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using System; 3 | 4 | namespace LiveDrmOperationsV3.Helpers 5 | { 6 | public class ConfigWrapper 7 | { 8 | private readonly IConfiguration _config; 9 | 10 | public ConfigWrapper(IConfiguration config, string azureRegion = null) 11 | { 12 | _config = config; 13 | AzureRegionCode = azureRegion ?? _config["AzureRegion"]; 14 | } 15 | 16 | public string SubscriptionId => _config["SubscriptionId"]; 17 | 18 | public string ResourceGroupFinalName => _config["ResourceGroupFinalName"]; 19 | 20 | public string ResourceGroup => _config["ResourceGroup"] + (string.IsNullOrEmpty(ResourceGroupFinalName) ? AzureRegionCode : ""); 21 | 22 | public string AccountName => _config["AccountName"] + AzureRegionCode; 23 | 24 | public string AadTenantId => _config["AadTenantId"]; 25 | 26 | public string AadClientId => _config["AadClientId"]; 27 | 28 | public string AadClientSecret => _config["AadClientSecret"] ?? _config["AadSecret"]; 29 | 30 | public Uri ArmAadAudience => _config["ArmAadAudience"] != null ? new Uri(_config["ArmAadAudience"]) : null; 31 | 32 | public Uri AadEndpoint => _config["AadEndpoint"] != null ? new Uri(_config["AadEndpoint"]) : null; 33 | 34 | public Uri ArmEndpoint => _config["ArmEndpoint"] != null ? new Uri(_config["ArmEndpoint"]) : null; 35 | 36 | public string Region 37 | { 38 | get 39 | { 40 | if (AzureRegionCode == null) 41 | return _config["Region"]; 42 | switch (AzureRegionCode) // codes as defined in AMS Streaming Endpoint hostname - to be completed 43 | { 44 | case "euno": 45 | case "no": 46 | return "North Europe"; 47 | 48 | case "euwe": 49 | case "we": 50 | return "West Europe"; 51 | 52 | default: 53 | return _config["Region"]; 54 | } 55 | } 56 | } 57 | 58 | public string AzureRegionCode { get; } 59 | 60 | public string LiveIngestAccessToken => _config["LiveIngestAccessToken"]; 61 | 62 | public string IrdetoUserName => _config["IrdetoUserName"]; 63 | 64 | public string IrdetoPassword => _config["IrdetoPassword"]; 65 | 66 | public string IrdetoAccountId => _config["IrdetoAccountId"]; 67 | 68 | public string IrdetoSoapService => _config["IrdetoSoapService"]; 69 | 70 | public string IrdetoPlayReadyLAURL => _config["IrdetoPlayReadyLAURL"]; 71 | 72 | public string IrdetoWidevineLAURL => _config["IrdetoWidevineLAURL"]; 73 | 74 | public string IrdetoFairPlayLAURL => _config["IrdetoFairPlayLAURL"]; 75 | 76 | public string IrdetoFairPlayCertificateUrl => _config["IrdetoFairPlayCertificateUrl"]; 77 | 78 | } 79 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/Helpers/copyBlobHelpers.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v2 - Functions 3 | // 4 | // Shared Library 5 | // 6 | 7 | using Microsoft.Azure.Storage; 8 | using Microsoft.Azure.Storage.Auth; 9 | using Microsoft.Azure.Storage.Blob; 10 | using System; 11 | 12 | namespace LiveDrmOperationsV3.Helpers 13 | { 14 | public class CopyBlobHelpers 15 | { 16 | 17 | static public async void CopyBlobAsync(CloudBlob sourceBlob, CloudBlob destinationBlob) 18 | { 19 | var signature = sourceBlob.GetSharedAccessSignature(new SharedAccessBlobPolicy 20 | { 21 | Permissions = SharedAccessBlobPermissions.Read, 22 | SharedAccessExpiryTime = DateTime.UtcNow.AddHours(24) 23 | }); 24 | await destinationBlob.StartCopyAsync(new Uri(sourceBlob.Uri.AbsoluteUri + signature)); 25 | } 26 | 27 | static public CloudBlobContainer GetCloudBlobContainer(string storageAccountName, string storageAccountKey, string containerName) 28 | { 29 | 30 | CloudStorageAccount sourceStorageAccount = new CloudStorageAccount(new StorageCredentials(storageAccountName, storageAccountKey), true); 31 | 32 | CloudBlobClient sourceCloudBlobClient = sourceStorageAccount.CreateCloudBlobClient(); 33 | 34 | return sourceCloudBlobClient.GetContainerReference(containerName); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net5.0 4 | v3 5 | 6 | 7 | 8 | 9 | 10 | 11 | PreserveNewest 12 | Never 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | PreserveNewest 26 | 27 | 28 | PreserveNewest 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/LiveFunctions/delete-streaming-locator.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 Functions 3 | // 4 | // delete-streaming-locator - This function delete a a streaming locator (to unpublish) 5 | // 6 | /* 7 | ```c# 8 | Input : 9 | { 10 | "streamingLocatorName": "locator-c03de9fe-dd04", 11 | "azureRegion": "euwe" or "we" or "euno" or "no" or "euwe,euno" or "we,no" 12 | // optional. If this value is set, then the AMS account name and resource group are appended with this value. 13 | // Resource name is not changed if "ResourceGroupFinalName" in app settings is to a value non empty. 14 | // This feature is useful if you want to manage several AMS account in different regions. 15 | // if two regions are sepecified using a comma as a separator, then the function will operate in the two regions at the same time 16 | // Note: the service principal must work with all this accounts 17 | } 18 | 19 | Output: 20 | { 21 | "success": true, 22 | "errorMessage" : "", 23 | "operationsVersion": "1.0.0.5" 24 | } 25 | 26 | 27 | ``` 28 | */ 29 | 30 | using LiveDrmOperationsV3.Helpers; 31 | using Microsoft.AspNetCore.Http; 32 | using Microsoft.AspNetCore.Mvc; 33 | using Microsoft.Azure.Management.Media; 34 | using Microsoft.Azure.WebJobs; 35 | using Microsoft.Azure.WebJobs.Extensions.Http; 36 | using Microsoft.Extensions.Configuration; 37 | using Microsoft.Extensions.Logging; 38 | using Newtonsoft.Json; 39 | using Newtonsoft.Json.Linq; 40 | using System; 41 | using System.Collections.Generic; 42 | using System.IO; 43 | using System.Linq; 44 | using System.Reflection; 45 | using System.Threading.Tasks; 46 | 47 | namespace LiveDrmOperationsV3 48 | { 49 | public static class DeleteStreamingLocator 50 | { 51 | [FunctionName("delete-streaming-locator")] 52 | public static async Task Run( 53 | [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] 54 | HttpRequest req, ILogger log) 55 | { 56 | MediaServicesHelpers.LogInformation(log, "C# HTTP trigger function processed a request."); 57 | 58 | dynamic data; 59 | try 60 | { 61 | data = JsonConvert.DeserializeObject(new StreamReader(req.Body).ReadToEnd()); 62 | } 63 | catch (Exception ex) 64 | { 65 | return IrdetoHelpers.ReturnErrorException(log, ex); 66 | } 67 | 68 | var streamingLocatorName = (string)data.streamingLocatorName; 69 | if (streamingLocatorName == null) 70 | return IrdetoHelpers.ReturnErrorException(log, "Error - please pass streamingLocatorName in the JSON"); 71 | 72 | 73 | // Azure region management 74 | var azureRegions = new List(); 75 | if ((string)data.azureRegion != null) 76 | { 77 | azureRegions = ((string)data.azureRegion).Split(',').ToList(); 78 | } 79 | else 80 | { 81 | azureRegions.Add((string)null); 82 | } 83 | 84 | foreach (var region in azureRegions) 85 | { 86 | ConfigWrapper config = null; 87 | 88 | try 89 | { 90 | config = new ConfigWrapper(new ConfigurationBuilder() 91 | .SetBasePath(Directory.GetCurrentDirectory()) 92 | .AddEnvironmentVariables() 93 | .Build(), 94 | region 95 | ); 96 | } 97 | catch (Exception ex) 98 | { 99 | return IrdetoHelpers.ReturnErrorException(log, ex); 100 | } 101 | 102 | MediaServicesHelpers.LogInformation(log, "config loaded.", region); 103 | MediaServicesHelpers.LogInformation(log, "connecting to AMS account : " + config.AccountName, region); 104 | 105 | var client = await MediaServicesHelpers.CreateMediaServicesClientAsync(config); 106 | // Set the polling interval for long running operations to 2 seconds. 107 | // The default value is 30 seconds for the .NET client SDK 108 | client.LongRunningOperationRetryTimeout = 2; 109 | 110 | try 111 | { 112 | client.StreamingLocators.Delete(config.ResourceGroup, config.AccountName, streamingLocatorName); 113 | 114 | } 115 | catch (Exception ex) 116 | { 117 | return IrdetoHelpers.ReturnErrorException(log, ex); 118 | } 119 | 120 | } 121 | 122 | var response = new JObject 123 | { 124 | {"streamingLocatorName", streamingLocatorName}, 125 | {"success", true}, 126 | { 127 | "operationsVersion", 128 | AssemblyName.GetAssemblyName(Assembly.GetExecutingAssembly().Location).Version.ToString() 129 | } 130 | }; 131 | 132 | return new OkObjectResult( 133 | response.ToString() 134 | ); 135 | 136 | } 137 | } 138 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/LiveFunctions/ping.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 Functions 3 | // 4 | // Ping - This function returns 200 (used by trafic manager) 5 | // 6 | 7 | 8 | using Microsoft.Azure.WebJobs; 9 | using Microsoft.Azure.WebJobs.Extensions.Http; 10 | using Microsoft.Extensions.Logging; 11 | using System.Net; 12 | using System.Net.Http; 13 | using System.Threading.Tasks; 14 | 15 | namespace LiveDrmOperationsV3 16 | { 17 | public static class Ping 18 | { 19 | [FunctionName("ping")] 20 | public static async Task Run( 21 | [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] 22 | HttpRequestMessage req, ILogger log) 23 | { 24 | return req.CreateResponse(HttpStatusCode.OK); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/LiveFunctions/update-settings.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 Functions 3 | // 4 | // update-settings - This function creates a document in Cosmos to store the settings for a live event (optional) 5 | // 6 | /* 7 | ```c# 8 | Input : 9 | { 10 | "liveEventName": "TEST", 11 | "urn": "urn:customer:video:e8927fcf-e1a0-0001-7edd-1eaaaaaa", 12 | "vendor": "Customer", 13 | "baseStorageName": "storagename", 14 | "archiveWindowLength": 10, 15 | "useStaticHostname": true, 16 | "lowLatency": false, 17 | "liveEventInputACL": [ 18 | "192.168.0.0/24", 19 | "86.246.149.14" 20 | ], 21 | "liveEventPreviewACL": [ 22 | "192.168.0.0/24", 23 | "86.246.149.14" 24 | ], 25 | "playerJSONData": null 26 | } 27 | 28 | Output: 29 | { 30 | "success": true, 31 | "errorMessage" : "", 32 | "operationsVersion": "1.0.0.5" 33 | } 34 | 35 | 36 | ``` 37 | */ 38 | 39 | using LiveDrmOperationsV3.Helpers; 40 | using LiveDrmOperationsV3.Models; 41 | using Microsoft.AspNetCore.Http; 42 | using Microsoft.AspNetCore.Mvc; 43 | using Microsoft.Azure.WebJobs; 44 | using Microsoft.Azure.WebJobs.Extensions.Http; 45 | using Microsoft.Extensions.Configuration; 46 | using Microsoft.Extensions.Logging; 47 | using Newtonsoft.Json; 48 | using Newtonsoft.Json.Linq; 49 | using System; 50 | using System.IO; 51 | using System.Reflection; 52 | using System.Threading.Tasks; 53 | 54 | namespace LiveDrmOperationsV3 55 | { 56 | public static class UpdateSettings 57 | { 58 | // This version registers keys in irdeto backend. For FairPlay and rpv3 59 | 60 | [FunctionName("update-settings")] 61 | public static async Task Run( 62 | [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] 63 | HttpRequest req, ILogger log) 64 | { 65 | log.LogInformation("C# HTTP trigger function processed a request."); 66 | 67 | bool success = true; 68 | 69 | var requestBody = new StreamReader(req.Body).ReadToEnd(); 70 | 71 | LiveEventSettingsInfo settings = null; 72 | try 73 | { 74 | settings = (LiveEventSettingsInfo)JsonConvert.DeserializeObject(requestBody, 75 | typeof(LiveEventSettingsInfo)); 76 | } 77 | catch (Exception ex) 78 | { 79 | return IrdetoHelpers.ReturnErrorException(log, ex); 80 | } 81 | 82 | 83 | ConfigWrapper config = null; 84 | try 85 | { 86 | config = new ConfigWrapper( 87 | new ConfigurationBuilder() 88 | .SetBasePath(Directory.GetCurrentDirectory()) 89 | .AddEnvironmentVariables() 90 | .Build() 91 | ); 92 | } 93 | catch (Exception ex) 94 | { 95 | return IrdetoHelpers.ReturnErrorException(log, ex); 96 | } 97 | 98 | log.LogInformation("config loaded."); 99 | 100 | try 101 | { 102 | if (!await CosmosHelpers.CreateOrUpdateSettingsDocument(settings)) 103 | { 104 | log.LogWarning("Cosmos access not configured or error."); 105 | success = false; 106 | } 107 | } 108 | catch (Exception ex) 109 | { 110 | return IrdetoHelpers.ReturnErrorException(log, ex); 111 | } 112 | 113 | var response = new JObject 114 | { 115 | {"Success", success}, 116 | { 117 | "OperationsVersion", 118 | AssemblyName.GetAssemblyName(Assembly.GetExecutingAssembly().Location).Version.ToString() 119 | } 120 | }; 121 | 122 | return new OkObjectResult( 123 | response.ToString() 124 | ); 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/ManifestTemplate/manifest.ism: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/Models/BaseModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LiveDRMOperationsV3.Models 4 | { 5 | // Base model for other models. 6 | // It implements a partitionKey property always set to the same value - used for Cosmos 7 | public class BaseModel 8 | { 9 | public static string DefaultPartitionValue = "live"; 10 | 11 | [JsonProperty(PropertyName = "partitionKey", NullValueHandling = NullValueHandling.Ignore)] 12 | public virtual string PartitionKey => DefaultPartitionValue; 13 | } 14 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/Models/GeneralOutputInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection; 3 | using LiveDrmOperationsV3.Helpers; 4 | using LiveDRMOperationsV3.Models; 5 | using Microsoft.Azure.Management.Media.Models; 6 | using Newtonsoft.Json; 7 | 8 | namespace LiveDrmOperationsV3.Models 9 | { 10 | 11 | public class Drm 12 | { 13 | [JsonProperty("type")] public string Type { get; set; } 14 | 15 | [JsonProperty("licenseUrl")] public string LicenseUrl { get; set; } 16 | 17 | [JsonProperty("certificateUrl", NullValueHandling = NullValueHandling.Ignore)] public string CertificateUrl { get; set; } 18 | 19 | [JsonProperty("protocols")] public List Protocols { get; set; } 20 | } 21 | 22 | public class UrlEntry 23 | { 24 | [JsonProperty("protocol")] public string Protocol { get; set; } 25 | 26 | [JsonProperty("url")] public string Url { get; set; } 27 | } 28 | 29 | public class StreamingLocatorEntry 30 | { 31 | [JsonProperty("streamingLocatorName")] public string StreamingLocatorName { get; set; } 32 | 33 | [JsonProperty("streamingPolicyName")] public string StreamingPolicyName { get; set; } 34 | 35 | [JsonProperty("cencKeyId")] public string CencKeyId { get; set; } 36 | 37 | [JsonProperty("cbcsKeyId")] public string CbcsKeyId { get; set; } 38 | 39 | [JsonProperty("drm")] public List Drm { get; set; } 40 | 41 | [JsonProperty("urls")] public List Urls { get; set; } 42 | } 43 | 44 | public class LiveOutputEntry 45 | { 46 | [JsonProperty("liveOutputName")] public string LiveOutputName { get; set; } 47 | 48 | [JsonProperty("archiveWindowLength")] public int ArchiveWindowLength { get; set; } 49 | 50 | [JsonProperty("assetName")] public string AssetName { get; set; } 51 | 52 | [JsonProperty("assetStorageAccountName")] public string AssetStorageAccountName { get; set; } 53 | 54 | [JsonProperty("resourceState")] public LiveOutputResourceState? ResourceState { get; set; } 55 | 56 | [JsonProperty("streamingLocators")] public List StreamingLocators { get; set; } 57 | } 58 | 59 | public class LiveEventEntry : BaseModel 60 | { 61 | [JsonProperty("liveEventName")] public string LiveEventName { get; set; } 62 | 63 | [JsonProperty("resourceState")] public string ResourceState { get; set; } 64 | 65 | [JsonProperty("useStaticHostname")] public bool? UseStaticHostname { get; set; } 66 | 67 | [JsonProperty("amsAccountName")] public string AMSAccountName { get; set; } 68 | 69 | [JsonProperty("region")] public string Region { get; set; } 70 | 71 | [JsonProperty("resourceGroup")] public string ResourceGroup { get; set; } 72 | 73 | [JsonProperty(PropertyName = "lowLatency")] public bool? LowLatency { get; set; } 74 | 75 | [JsonProperty(PropertyName = "id")] public string Id => (AMSAccountName + ":" + LiveEventName).ToLower(); 76 | 77 | [JsonProperty("input")] public List Input { get; set; } 78 | 79 | [JsonProperty("inputACL")] public List InputACL { get; set; } 80 | 81 | [JsonProperty("preview")] public List Preview { get; set; } 82 | 83 | [JsonProperty("previewACL")] public List PreviewACL { get; set; } 84 | 85 | [JsonProperty("liveOutputs")] public List LiveOutputs { get; set; } 86 | } 87 | 88 | public class GeneralOutputInfo 89 | { 90 | [JsonProperty("success")] public bool Success { get; set; } 91 | 92 | [JsonProperty("operationsVersion")] 93 | public string OperationsCodeVersion => 94 | AssemblyName.GetAssemblyName(Assembly.GetExecutingAssembly().Location).Version.ToString(); 95 | 96 | [JsonProperty("liveEvents")] public List LiveEvents { get; set; } 97 | } 98 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/Models/LiveEventSettingsInfo.cs: -------------------------------------------------------------------------------- 1 | using LiveDRMOperationsV3.Models; 2 | using Microsoft.Azure.Management.Media.Models; 3 | using Newtonsoft.Json; 4 | using System.Collections.Generic; 5 | 6 | namespace LiveDrmOperationsV3.Models 7 | { 8 | public class RedirectorStreamingEndpointData 9 | { 10 | [JsonProperty("streamingEndpointName")] public string StreamingEndpointName { get; set; } 11 | 12 | [JsonProperty("percentage")] public int Percentage { get; set; } 13 | } 14 | 15 | public class Dvr 16 | { 17 | [JsonProperty("enableByDefault")] public bool EnableByDefault { get; set; } 18 | 19 | [JsonProperty("allowed")] public bool Allowed { get; set; } 20 | } 21 | 22 | public class ResourceListOrder 23 | { 24 | [JsonProperty("dvr")] public Dvr Dvr { get; set; } 25 | 26 | [JsonProperty("encryptionSorted")] public bool EncryptionSorted { get; set; } 27 | 28 | [JsonProperty("defaultAzureRegion")] public string DefaultAzureRegion { get; set; } 29 | } 30 | 31 | public class PlayerJSONData 32 | { 33 | [JsonProperty("quality")] public string Quality { get; set; } 34 | 35 | [JsonProperty("encoding")] public string Encoding { get; set; } 36 | 37 | [JsonProperty("presentation")] public string Presentation { get; set; } 38 | 39 | [JsonProperty("live")] public bool Live { get; set; } 40 | 41 | [JsonProperty("mediaContainer")] public string MediaContainer { get; set; } 42 | 43 | [JsonProperty("audioCodec")] public string AudioCodec { get; set; } 44 | 45 | [JsonProperty("videoCodec")] public string VideoCodec { get; set; } 46 | 47 | [JsonProperty("resourceListOrder")] public ResourceListOrder ResourceListOrder { get; set; } 48 | } 49 | 50 | public class LiveEventSettingsInfo : BaseModel 51 | { 52 | public LiveEventSettingsInfo() 53 | { 54 | ArchiveWindowLength = 10; 55 | UseStaticHostname = false; 56 | InputProtocol = LiveEventInputProtocol.FragmentedMP4; 57 | AutoStart = true; 58 | LowLatency = false; 59 | } 60 | 61 | [JsonProperty("liveEventName")] public string LiveEventName { get; set; } 62 | 63 | [JsonProperty("urn")] public string Urn { get; set; } 64 | 65 | [JsonProperty("vendor")] public string Vendor { get; set; } 66 | 67 | [JsonProperty("akamaiHostname")] public string AkamaiHostname { get; set; } 68 | 69 | [JsonProperty("baseStorageName")] public string BaseStorageName { get; set; } 70 | 71 | [JsonIgnore] public string StorageName { get; set; } 72 | 73 | [JsonProperty("archiveWindowLength")] public int ArchiveWindowLength { get; set; } 74 | 75 | [JsonProperty("useStaticHostname")] public bool UseStaticHostname { get; set; } 76 | 77 | [JsonProperty("lowLatency")] public bool LowLatency { get; set; } 78 | 79 | [JsonIgnore] public LiveEventInputProtocol InputProtocol { get; set; } 80 | 81 | [JsonIgnore] public bool AutoStart { get; set; } 82 | 83 | [JsonProperty("liveEventInputACL")] public IList LiveEventInputACL { get; set; } 84 | 85 | [JsonProperty("liveEventPreviewACL")] public IList LiveEventPreviewACL { get; set; } 86 | 87 | [JsonProperty("playerJSONData")] public PlayerJSONData PlayerJSONData { get; set; } 88 | 89 | [JsonProperty("redirectorStreamingEndpointData")] public List RedirectorStreamingEndpointData { get; set; } 90 | 91 | [JsonProperty(PropertyName = "id")] public string Id => LiveEventName.ToLower(); 92 | } 93 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/Models/StreamingPolicy.cs: -------------------------------------------------------------------------------- 1 | using LiveDRMOperationsV3.Models; 2 | using Newtonsoft.Json; 3 | 4 | namespace LiveDrmOperationsV3.Models 5 | { 6 | public class StreamingPolicyInfo : BaseModel 7 | { 8 | [JsonProperty("streamingPolicyName")] public string StreamingPolicyName { get; set; } 9 | 10 | [JsonProperty("amsAccountName")] public string AMSAccountName { get; set; } 11 | 12 | [JsonProperty(PropertyName = "id")] public string Id => (AMSAccountName + ":" + Partition).ToLower(); 13 | 14 | [JsonProperty(PropertyName = "partitionKey", NullValueHandling = NullValueHandling.Ignore)] 15 | public override string PartitionKey => Partition; 16 | 17 | public StreamingPolicyInfo(bool isVod) 18 | { 19 | Partition = isVod ? AssetEntry.DefaultPartitionValue : BaseModel.DefaultPartitionValue; 20 | } 21 | 22 | [JsonIgnore] private string Partition { get; set; } 23 | } 24 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/Models/VodAssetInfo.cs: -------------------------------------------------------------------------------- 1 | using LiveDRMOperationsV3.Models; 2 | using Newtonsoft.Json; 3 | using System.Collections.Generic; 4 | using System.Reflection; 5 | 6 | namespace LiveDrmOperationsV3.Models 7 | { 8 | 9 | public partial class AssetEntry : BaseModel 10 | { 11 | public static readonly string DateFormat = "yyyyMMddTHH:mm:ssZ"; 12 | 13 | public new static string DefaultPartitionValue = "vod"; 14 | 15 | [JsonProperty("assetName")] 16 | public string AssetName { get; set; } 17 | 18 | [JsonProperty("assetStorageAccountName", NullValueHandling = NullValueHandling.Ignore)] 19 | public string AssetStorageAccountName { get; set; } 20 | 21 | [JsonProperty("streamingLocators", NullValueHandling = NullValueHandling.Ignore)] 22 | public List StreamingLocators { get; set; } 23 | 24 | [JsonProperty("amsAccountName", NullValueHandling = NullValueHandling.Ignore)] 25 | public string AMSAccountName { get; set; } 26 | 27 | [JsonProperty("region", NullValueHandling = NullValueHandling.Ignore)] 28 | public string Region { get; set; } 29 | 30 | [JsonProperty("resourceGroup", NullValueHandling = NullValueHandling.Ignore)] 31 | public string ResourceGroup { get; set; } 32 | 33 | [JsonProperty("urn", NullValueHandling = NullValueHandling.Ignore)] 34 | public string Urn { get; set; } 35 | 36 | [JsonProperty("createdTime", NullValueHandling = NullValueHandling.Ignore)] 37 | public string CreatedTime { get; set; } 38 | 39 | [JsonProperty("semaphore", NullValueHandling = NullValueHandling.Ignore)] 40 | public VodSemaphore Semaphore { get; set; } 41 | 42 | [JsonIgnore] public string ContentId { get; set; } 43 | 44 | [JsonProperty(PropertyName = "id", NullValueHandling = NullValueHandling.Ignore)] 45 | public string Id => (AMSAccountName + ":" + AssetName).ToLower(); 46 | 47 | [JsonProperty(PropertyName = "partitionKey", NullValueHandling = NullValueHandling.Ignore)] 48 | public override string PartitionKey => (ContentId ?? "UND").ToLower(); 49 | } 50 | 51 | 52 | public partial class AssetEntry 53 | { 54 | public static AssetEntry FromJson(string json) => JsonConvert.DeserializeObject(json, Converter.Settings); 55 | } 56 | 57 | 58 | public class VodAssetInfo 59 | { 60 | [JsonProperty("success")] public bool Success { get; set; } 61 | 62 | [JsonProperty("operationsVersion")] 63 | public string OperationsCodeVersion => 64 | AssemblyName.GetAssemblyName(Assembly.GetExecutingAssembly().Location).Version.ToString(); 65 | 66 | [JsonProperty("assets")] public List Assets { get; set; } 67 | } 68 | 69 | public class VodAssetInfoSimple 70 | { 71 | [JsonProperty("success")] public bool Success { get; set; } 72 | 73 | [JsonProperty("operationsVersion")] 74 | public string OperationsCodeVersion => 75 | AssemblyName.GetAssemblyName(Assembly.GetExecutingAssembly().Location).Version.ToString(); 76 | 77 | [JsonProperty("createdLocatorName")] public string CreatedLocatorName { get; set; } 78 | 79 | [JsonProperty("createdLocatorPath")] public string CreatedLocatorPath { get; set; } 80 | 81 | [JsonProperty("asset")] public AssetEntry Asset { get; set; } 82 | } 83 | 84 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/Models/VodResource.cs: -------------------------------------------------------------------------------- 1 | using LiveDRMOperationsV3.Models; 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace LiveDrmOperationsV3.Models 7 | { 8 | 9 | public partial class DrmList 10 | { 11 | 12 | [JsonProperty("type")] 13 | public string Type { get; set; } 14 | 15 | [JsonProperty("licenseUrl")] 16 | public string LicenseUrl { get; set; } 17 | 18 | [JsonProperty("certificateUrl")] 19 | public string CertificateUrl { get; set; } 20 | } 21 | 22 | public partial class Subtitles 23 | { 24 | [JsonProperty("url")] 25 | public string Url { get; set; } 26 | 27 | [JsonProperty("textLanguage")] 28 | public string TextLanguage { get; set; } 29 | 30 | [JsonProperty("textTitle")] 31 | public string TextTitle { get; set; } 32 | } 33 | 34 | public partial class ResourceList 35 | { 36 | 37 | [JsonProperty("url")] 38 | public string Url { get; set; } 39 | 40 | [JsonProperty("drmList")] 41 | public IList DrmList { get; set; } 42 | 43 | [JsonProperty("quality")] 44 | public string Quality { get; set; } 45 | 46 | [JsonProperty("protocol")] 47 | public string Protocol { get; set; } 48 | 49 | [JsonProperty("mimeType")] 50 | public string MimeType { get; set; } 51 | 52 | [JsonProperty("videoCodec")] 53 | public string VideoCodec { get; set; } 54 | 55 | [JsonProperty("audioCodec")] 56 | public string AudioCodec { get; set; } 57 | 58 | [JsonProperty("mediaContainer")] 59 | public string MediaContainer { get; set; } 60 | 61 | [JsonProperty("live")] 62 | public bool Live { get; set; } 63 | 64 | [JsonProperty("subTitles")] 65 | public IList SubTitles { get; set; } 66 | } 67 | 68 | public partial class VodResource : BaseModel 69 | { 70 | 71 | [JsonProperty("urn")] 72 | public string Urn { get; set; } 73 | 74 | [JsonProperty("resourceList")] 75 | public IList ResourceList { get; set; } 76 | 77 | [JsonIgnore] public AssetEntry MainAsset { get; set; } 78 | 79 | [JsonProperty(PropertyName = "id", NullValueHandling = NullValueHandling.Ignore)] 80 | public string Id => ((new Uri(ResourceList[0]?.Url)).Host + "-" + (new Uri(ResourceList[0]?.Url)).Segments[1].Replace("/", string.Empty)).ToLower(); 81 | 82 | [JsonProperty(PropertyName = "partitionKey", NullValueHandling = NullValueHandling.Ignore)] 83 | public override string PartitionKey => (MainAsset.Semaphore.DrmContentId ?? DefaultPartitionValue).ToLower(); 84 | 85 | public new static string DefaultPartitionValue = "vod"; 86 | } 87 | 88 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/VodFunctions/generate-resource.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 Functions 3 | // 4 | // generate-resource - This function generate a json structure to be used by the customer in their backend 5 | // 6 | /* 7 | ```c# 8 | Input : 9 | { 10 | "assetMain": , 11 | "assetSub": , 12 | "cdnHostName": "customertest-euwe.akamaized.net" 13 | } 14 | 15 | 16 | Output: 17 | 18 | ``` 19 | */ 20 | 21 | using LiveDrmOperationsV3.Helpers; 22 | using LiveDrmOperationsV3.Models; 23 | using Microsoft.AspNetCore.Http; 24 | using Microsoft.AspNetCore.Mvc; 25 | using Microsoft.Azure.WebJobs; 26 | using Microsoft.Azure.WebJobs.Extensions.Http; 27 | using Microsoft.Extensions.Logging; 28 | using Newtonsoft.Json; 29 | using Newtonsoft.Json.Linq; 30 | using System; 31 | using System.Collections.Generic; 32 | using System.IO; 33 | using System.Linq; 34 | using System.Threading.Tasks; 35 | 36 | namespace LiveDrmOperationsV3 37 | { 38 | public static class generateresource 39 | { 40 | [FunctionName("generate-resource")] 41 | public static async Task Run( 42 | [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] 43 | HttpRequest req, ILogger log) 44 | { 45 | log.LogInformation("C# HTTP trigger function processed a request."); 46 | 47 | dynamic data; 48 | try 49 | { 50 | data = JsonConvert.DeserializeObject(new StreamReader(req.Body).ReadToEnd()); 51 | } 52 | catch (Exception ex) 53 | { 54 | return IrdetoHelpers.ReturnErrorException(log, ex); 55 | } 56 | 57 | AssetEntry assetEntry1 = null; 58 | 59 | if (data.assetMain != null) 60 | { 61 | assetEntry1 = ((JObject)data?.assetMain).ToObject(); 62 | } 63 | 64 | AssetEntry assetEntry2 = null; 65 | if (data.assetSub != null) 66 | { 67 | assetEntry2 = ((JObject)data?.assetSub).ToObject(); 68 | } 69 | 70 | string newHostName = (string)data.cdnHostName; 71 | if (string.IsNullOrWhiteSpace(newHostName)) newHostName = null; 72 | 73 | VodResource vodResource = new VodResource() 74 | { 75 | Urn = assetEntry1.Urn, 76 | ResourceList = new List(), 77 | MainAsset = assetEntry1 78 | }; 79 | 80 | 81 | var subtitlesList = new List(); 82 | if (assetEntry2 != null) 83 | { 84 | var locator2 = assetEntry2.StreamingLocators.FirstOrDefault(); 85 | var subtitlesUrl = locator2?.Urls.Where(p => p.Protocol == "Download"); 86 | var subtitlesSema = assetEntry2?.Semaphore.Files.Where(f => f.CopyToSubAsset); 87 | 88 | var query = from subInfo in subtitlesSema 89 | join sub in subtitlesUrl on subInfo.FileName equals (new Uri(sub.Url)).Segments[(new Uri(sub.Url)).Segments.Length - 1] 90 | select new Subtitles { Url = MediaServicesHelpers.UpdateHostNameIfNeeded(newHostName, sub.Url), TextLanguage = subInfo.TextLanguage, TextTitle = subInfo.TextTitle }; 91 | subtitlesList = query.ToList(); 92 | } 93 | 94 | var locator1 = assetEntry1.StreamingLocators.FirstOrDefault(); 95 | 96 | var resDashCsf = new ResourceList() 97 | { 98 | Url = MediaServicesHelpers.UpdateHostNameIfNeeded(newHostName, locator1?.Urls.Where(u => u.Protocol == "DashCsf").FirstOrDefault()?.Url), 99 | Protocol = "DASH", 100 | MimeType = "application/dash+xml", 101 | VideoCodec = "H264", 102 | AudioCodec = "AAC", 103 | MediaContainer = "MP4", 104 | Quality = "SD", 105 | Live = false, 106 | DrmList = new List() { 107 | new DrmList(){ Type = "PlayReady", LicenseUrl =locator1?.Drm?.Where(d=> d.Type=="PlayReady").FirstOrDefault()?.LicenseUrl }, 108 | new DrmList(){ Type = "Widevine", LicenseUrl =locator1?.Drm?.Where(d=> d.Type=="Widevine").FirstOrDefault()?.LicenseUrl } 109 | }, 110 | SubTitles = subtitlesList 111 | }; 112 | 113 | vodResource.ResourceList.Add(resDashCsf); 114 | 115 | var resHlsTs = new ResourceList() 116 | { 117 | Url = MediaServicesHelpers.UpdateHostNameIfNeeded(newHostName, locator1?.Urls.Where(u => u.Protocol == "HlsTs").FirstOrDefault()?.Url), 118 | Protocol = "HLS", 119 | MimeType = "application/x-mpegURL", 120 | VideoCodec = "H264", 121 | AudioCodec = "AAC", 122 | MediaContainer = "MP4", 123 | Quality = "SD", 124 | Live = false, 125 | DrmList = new List() { 126 | new DrmList(){ 127 | Type = "FairPlay", 128 | LicenseUrl =locator1.Drm.Where(d=> d.Type=="FairPlay").FirstOrDefault().LicenseUrl, 129 | CertificateUrl = locator1.Drm.Where(d=> d.Type=="FairPlay").FirstOrDefault().CertificateUrl 130 | }, 131 | }, 132 | SubTitles = subtitlesList 133 | }; 134 | vodResource.ResourceList.Add(resHlsTs); 135 | 136 | 137 | // let's write it to Cosmos 138 | if (!await CosmosHelpers.CreateOrUpdateVODResourceDocument(vodResource)) 139 | log.LogWarning("Cosmos access not configured."); 140 | 141 | 142 | return new OkObjectResult( 143 | JsonConvert.SerializeObject(vodResource, Formatting.Indented) 144 | ); 145 | } 146 | } 147 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/VodFunctions/list-assets-startwith.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 Functions 3 | // 4 | // list-assets-startwith 5 | /* 6 | This function creates an empty asset. 7 | 8 | 9 | ```c# 10 | Input: 11 | { 12 | "assetNameStartsWith" : "movie-", // 13 | "azureRegion": "euwe" or "we" or "euno" or "no" or "euwe,euno" or "we,no" 14 | // optional. If this value is set, then the AMS account name and resource group are appended with this value. 15 | // Resource name is not changed if "ResourceGroupFinalName" in app settings is to a value non empty. 16 | // This feature is useful if you want to manage several AMS account in different regions. 17 | // if two regions are sepecified using a comma as a separator, then the function will operate in the two regions at the same time. With this function, the live event will be deleted from the two regions. 18 | // Note: the service principal must work with all this accounts 19 | } 20 | 21 | 22 | Output: 23 | { 24 | "success": true, 25 | "assetNames": [ 26 | "92ccbf1edb-input-59894", 27 | "92ccbf1edb-input-59894-ContentAwareEncode-output-59894" 28 | ], 29 | "operationsVersion": "1.0.1.0" 30 | } 31 | 32 | ``` 33 | */ 34 | 35 | using LiveDrmOperationsV3.Helpers; 36 | using Microsoft.AspNetCore.Http; 37 | using Microsoft.AspNetCore.Mvc; 38 | using Microsoft.Azure.Management.Media; 39 | using Microsoft.Azure.Management.Media.Models; 40 | using Microsoft.Azure.WebJobs; 41 | using Microsoft.Azure.WebJobs.Extensions.Http; 42 | using Microsoft.Extensions.Configuration; 43 | using Microsoft.Extensions.Logging; 44 | using Microsoft.Rest.Azure.OData; 45 | using Newtonsoft.Json; 46 | using Newtonsoft.Json.Linq; 47 | using System; 48 | using System.Collections.Generic; 49 | using System.IO; 50 | using System.Linq; 51 | using System.Reflection; 52 | using System.Threading.Tasks; 53 | 54 | 55 | namespace LiveDrmOperationsV3 56 | { 57 | public static class ListAssetsStartwith 58 | { 59 | [FunctionName("list-assets-startwith")] 60 | public static async Task Run( 61 | [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] 62 | HttpRequest req, ILogger log, Microsoft.Azure.WebJobs.ExecutionContext execContext) 63 | { 64 | MediaServicesHelpers.LogInformation(log, "C# HTTP trigger function processed a request."); 65 | 66 | dynamic data; 67 | try 68 | { 69 | data = JsonConvert.DeserializeObject(new StreamReader(req.Body).ReadToEnd()); 70 | } 71 | catch (Exception ex) 72 | { 73 | return IrdetoHelpers.ReturnErrorException(log, ex); 74 | } 75 | 76 | var assetNameStartsWith = (string)data.assetNameStartsWith; 77 | if (assetNameStartsWith == null) 78 | return IrdetoHelpers.ReturnErrorException(log, "Error - please pass assetNameStartsWith in the JSON"); 79 | 80 | List assets = new List(); 81 | 82 | // Azure region management 83 | var azureRegions = new List(); 84 | if ((string)data.azureRegion != null) 85 | { 86 | azureRegions = ((string)data.azureRegion).Split(',').ToList(); 87 | } 88 | else 89 | { 90 | azureRegions.Add((string)null); 91 | } 92 | 93 | 94 | foreach (var region in azureRegions) 95 | { 96 | ConfigWrapper config = new ConfigWrapper(new ConfigurationBuilder() 97 | .SetBasePath(Directory.GetCurrentDirectory()) 98 | .AddEnvironmentVariables() 99 | .Build(), 100 | region 101 | ); 102 | 103 | MediaServicesHelpers.LogInformation(log, "config loaded.", region); 104 | MediaServicesHelpers.LogInformation(log, "connecting to AMS account : " + config.AccountName, region); 105 | 106 | var client = await MediaServicesHelpers.CreateMediaServicesClientAsync(config); 107 | // Set the polling interval for long running operations to 2 seconds. 108 | // The default value is 30 seconds for the .NET client SDK 109 | client.LongRunningOperationRetryTimeout = 2; 110 | 111 | MediaServicesHelpers.LogInformation(log, "asset name starts : " + assetNameStartsWith, region); 112 | 113 | try 114 | { 115 | ODataQuery query = new ODataQuery(); 116 | string search = "'" + assetNameStartsWith + "'"; 117 | query.Filter = "name gt " + search.Substring(0, search.Length - 2) + char.ConvertFromUtf32(char.ConvertToUtf32(search, search.Length - 2) - 1) + new string('z', 262 - search.Length) + "'" + " and name lt " + search.Substring(0, search.Length - 1) + new string('z', 262 - search.Length) + "'"; 118 | query.OrderBy = "Properties/Created"; 119 | var assetsResult = client.Assets.List(config.ResourceGroup, config.AccountName, query); 120 | 121 | assets = assetsResult.Select(a => a.Name).ToList(); 122 | } 123 | catch (Exception ex) 124 | { 125 | return IrdetoHelpers.ReturnErrorException(log, ex); 126 | } 127 | } 128 | 129 | var response = new JObject 130 | { 131 | {"success", true}, 132 | {"assetNames", new JArray(assets)}, 133 | { 134 | "operationsVersion", 135 | AssemblyName.GetAssemblyName(Assembly.GetExecutingAssembly().Location).Version.ToString() 136 | } 137 | }; 138 | 139 | return new OkObjectResult( 140 | response 141 | ); 142 | } 143 | } 144 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "applicationInsights": { 5 | "samplingExcludedTypes": "Request", 6 | "samplingSettings": { 7 | "isEnabled": true 8 | } 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LiveAndVodDRMOperationsV3/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "AzureWebJobsStorage": "UseDevelopmentStorage=true", 5 | "FUNCTIONS_WORKER_RUNTIME": "dotnet", 6 | "AzureWebJobsDashboard": "UseDevelopmentStorage=true", 7 | "AadClientId": "value", 8 | "AadEndpoint": "https://login.microsoftonline.com", 9 | "AadSecret": "value", 10 | "AadTenantId": "value", 11 | "AccountName": "value", 12 | "ArmAadAudience": "https://management.core.windows.net/", 13 | "ArmEndpoint": "https://management.azure.com/", 14 | "Region": "value", 15 | "AzureRegion": "euwe", 16 | "ResourceGroup": "value", 17 | "ResourceGroupFinalName": "true", 18 | "SubscriptionId": "value", 19 | "IrdetoUserName": "value", 20 | "IrdetoPassword": "value", 21 | "IrdetoAccountId": "value", 22 | "IrdetoSoapService": "https://test.ott.irdeto.com/LiveDRMService/livedrmservice.asmx", 23 | "IrdetoPlayReadyLAURL": "https://test.ott.irdeto.com/licenseServer/playready/v1/customer/license?contentId={AlternativeMediaId}", 24 | "IrdetoWidevineLAURL": "https://test.ott.irdeto.com/licenseServer/widevine/v1/customer/license?contentId={AlternativeMediaId}", 25 | "IrdetoFairPlayLAURL": "skd://test.ott.irdeto.com/licenseServer/streaming/v1/customer/getckc?contentId={AlternativeMediaId}&keyId={ContentKeyId}", 26 | "IrdetoFairPlayCertificateUrl": "https://test.ott.irdeto.com/licenseServer/streaming/v1/customer/getcertificate?applicationId=stage", 27 | "LiveIngestAccessToken": "value", 28 | "CosmosDBAccountEndpoint": "value", 29 | "CosmosDBAccountKey": "value", 30 | "CosmosDB": "liveDRMStreaming", 31 | "CosmosCollectionSettings": "liveEventSettings", 32 | "CosmosCollectionOutputs": "liveEventOutputInfo", 33 | "CosmosCollectionVODAssets": "vodAssets", 34 | "CosmosCollectionStreamingPolicies": "streamingPolicies", 35 | "CosmosCollectionVODResources": "vodResources", 36 | "AllowClearStream": "true" 37 | } 38 | } -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LogicApps/preencodedasset-workflow-deploy.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/media-services-v3-dotnet-core-functions-integration/4f0d6a3e0de03777d8ce49b4a7721cc0b6255ccc/LiveAndVodDRMOperationsV3/LogicApps/preencodedasset-workflow-deploy.json -------------------------------------------------------------------------------- /LiveAndVodDRMOperationsV3/LogicApps/semaphore-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "encodedAsset": true, 3 | "transformName": "myTransform", 4 | "startTime": "20180214T13:00:00Z", 5 | "endTime": "20290214T13:00:00Z", 6 | "urn": "asset URN", 7 | "drmContentId": "vodMajor1", 8 | "clearStream": false, 9 | "files": [ 10 | { 11 | "fileName": "video-400.mp4", 12 | "containsVideo": true, 13 | "containsAudio": true, 14 | "videoCodec":"H264", 15 | "audioCodec":"AAC", 16 | "mediaContainer": "MP4", 17 | "videoQuality": "SD" 18 | }, 19 | { 20 | "fileName": "video-700.mp4", 21 | "containsVideo": true, 22 | "containsAudio": true, 23 | "audioLanguage": "deu", 24 | "audioTitle ": "German audio" 25 | }, 26 | { 27 | "fileName": "video-1200.mp4", 28 | "containsVideo": true, 29 | "containsAudio": true, 30 | "audioLanguage": "eng" 31 | }, 32 | { 33 | "fileName": "subtitle-de.vtt", 34 | "containsText": true, 35 | "language": "deu", 36 | "copyToSubAsset" : true 37 | }, 38 | { 39 | "fileName": "subtitle-en.vtt", 40 | "containsText": true, 41 | "textLanguage": "eng", 42 | "textTitle": "English subtitles", 43 | "copyToSubAsset" : true 44 | }, 45 | { 46 | "fileName": "audio-en.mp4", 47 | "containsAudio": true, 48 | "audioLanguage ": "eng" 49 | } 50 | ] 51 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - csharp 5 | products: 6 | - azure 7 | - azure-functions 8 | - azure-media-services 9 | name: "Azure Media Services v3 - Serverless Workflows with Azure Functions and Logic Apps" 10 | description: "Projects that show how to integrate Azure Media Services with Azure Functions and Azure Logic Apps." 11 | azureDeploy: https://raw.githubusercontent.com/Azure-Samples/media-services-v3-dotnet-core-functions-integration/master/azuredeploy.json 12 | --- 13 | 14 | # Azure Media Services v3 - Serverless Workflows with Azure Functions & Logic Apps 15 | 16 | **IMPORTANT NOTE : Azure Media Services have been retired on June 30, 2024. Please see the [retirement guide](https://learn.microsoft.com/azure/media-services/latest/azure-media-services-retirement).** 17 | 18 | This repository contains projects that show how to integrate Azure Media Services with Azure Functions & Azure Logic Apps. 19 | These Media Services Functions examples are based on AMS REST API v3 on Azure Functions v3. Most of the functions can also be used from Logic Apps. 20 | 21 | This repository can be accessed directly using . 22 | 23 | ## Contents 24 | 25 | | Folder | Description | 26 | |-------------|-------------| 27 | | [Functions](/Functions)|**Updated March 2023** This sample exposes Azure Functions based on .NET 7.0 using the latest Media Services, Azure Functions and Identity SDKs. Deployment is done with an ARM template and GitHub Actions.| 28 | | [Encoding](/Encoding)|The sample exposes an Azure Function that encodes an Azure Storage blob with ffmpeg. Azure Functions Premium plan is recommended.| 29 | | [LiveAndVodDRMOperationsV3](/LiveAndVodDRMOperationsV3)|The sample exposes several Azure functions that can be used to manage live streaming and VOD with DRM with Azure Media Services v3, using Irdeto back-end to deliver the licenses.| 30 | | [advanced-vod-workflow](/advanced-vod-workflow)|This project contains advanced VOD media workflow examples of using Azure Functions with Azure Media Services v3. The project includes several folders of sample Azure Functions for use with Azure Media Services that show workflows related to ingesting content directly from blob storage, encoding, and writing content back to blob storage.| 31 | | [logic-app-using-workflow-functions](/logic-app-using-workflow-functions)|This project contains Logic Apps that converts-to-media-asset, encodes and publishes media files that you upload in an Azure Storage. It relies on Azure Functions and Azure Media Services.| 32 | 33 | ## Prerequisites for a sample Logic Apps deployments 34 | 35 | ### 1. Create an Azure Media Services account 36 | 37 | Create a Media Services account in your subscription if don't have it already ([follow this article](https://docs.microsoft.com/en-us/azure/media-services/latest/create-account-howto?tabs=portal)). 38 | 39 | ### 2. Create a Service Principal 40 | 41 | Create a Service Principal and save the password. It will be needed in step #4. To do so, go to the API tab in the account ([follow this article](https://docs.microsoft.com/en-us/azure/media-services/latest/access-api-howto?tabs=portal)). 42 | 43 | ### 3. Make sure the AMS streaming endpoint is started 44 | 45 | To enable streaming, go to the Azure portal, select the Azure Media Services account which has been created, and start the default streaming endpoint. 46 | 47 | ### 4. Deploy the Azure functions 48 | 49 | For the 'Functions" project, do not use the link below. Please see the dedicated [Readme](/Functions/README.md). 50 | 51 | If not already done : fork the repo, deploy Azure Functions and select the right project (IMPORTANT!). 52 | 53 | Note : if you never provided your GitHub account in the Azure portal before, the continuous integration probably will probably fail and you won't see the functions. In that case, you need to setup it manually. Go to your Azure Function App / Deployment / Deployment Center. Select GitHub as a source and configure it to use your fork. 54 | 55 | [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure-Samples%2Fmedia-services-v3-dotnet-core-functions-integration%2Fmaster%2Fazuredeploy.json) 56 | -------------------------------------------------------------------------------- /Tutorial/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Hosting; 4 | using Microsoft.Azure.Functions.Worker.Configuration; 5 | 6 | namespace TestFuncVS 7 | { 8 | public class Program 9 | { 10 | public static void Main() 11 | { 12 | var host = new HostBuilder() 13 | .ConfigureFunctionsWorkerDefaults() 14 | .Build(); 15 | 16 | host.Run(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Tutorial/README.md: -------------------------------------------------------------------------------- 1 | This project is used by the article 2 | https://docs.microsoft.com/en-us/azure/media-services/latest/integrate-azure-functions-dotnet-how-to 3 | -------------------------------------------------------------------------------- /Tutorial/TestFuncVS.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net5.0 4 | v3 5 | Exe 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | PreserveNewest 18 | 19 | 20 | PreserveNewest 21 | Never 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Tutorial/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "applicationInsights": { 5 | "samplingSettings": { 6 | "isEnabled": true, 7 | "excludedTypes": "Request" 8 | } 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /advanced-vod-workflow/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | local.settings.json 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary however generally it will be regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | .paket/paket.exe 250 | paket-files/ 251 | 252 | # FAKE - F# Make 253 | .fake/ 254 | 255 | # JetBrains Rider 256 | .idea/ 257 | *.sln.iml 258 | 259 | # CodeRush 260 | .cr/ 261 | 262 | # Python Tools for Visual Studio (PTVS) 263 | __pycache__/ 264 | *.pyc -------------------------------------------------------------------------------- /advanced-vod-workflow/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-azuretools.vscode-azurefunctions", 4 | "ms-dotnettools.csharp" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /advanced-vod-workflow/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Attach to .NET Functions", 6 | "type": "coreclr", 7 | "request": "attach", 8 | "processId": "${command:azureFunctions.pickProcess}" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /advanced-vod-workflow/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "azureFunctions.deploySubpath": "bin/Release/netcoreapp3.1/publish", 3 | "azureFunctions.projectLanguage": "C#", 4 | "azureFunctions.projectRuntime": "~3", 5 | "debug.internalConsoleOptions": "neverOpen", 6 | "azureFunctions.preDeployTask": "publish (functions)" 7 | } -------------------------------------------------------------------------------- /advanced-vod-workflow/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "clean (functions)", 6 | "command": "dotnet", 7 | "args": [ 8 | "clean", 9 | "/property:GenerateFullPaths=true", 10 | "/consoleloggerparameters:NoSummary" 11 | ], 12 | "type": "process", 13 | "problemMatcher": "$msCompile" 14 | }, 15 | { 16 | "label": "build (functions)", 17 | "command": "dotnet", 18 | "args": [ 19 | "build", 20 | "/property:GenerateFullPaths=true", 21 | "/consoleloggerparameters:NoSummary" 22 | ], 23 | "type": "process", 24 | "dependsOn": "clean (functions)", 25 | "group": { 26 | "kind": "build", 27 | "isDefault": true 28 | }, 29 | "problemMatcher": "$msCompile" 30 | }, 31 | { 32 | "label": "clean release (functions)", 33 | "command": "dotnet", 34 | "args": [ 35 | "clean", 36 | "--configuration", 37 | "Release", 38 | "/property:GenerateFullPaths=true", 39 | "/consoleloggerparameters:NoSummary" 40 | ], 41 | "type": "process", 42 | "problemMatcher": "$msCompile" 43 | }, 44 | { 45 | "label": "publish (functions)", 46 | "command": "dotnet", 47 | "args": [ 48 | "publish", 49 | "--configuration", 50 | "Release", 51 | "/property:GenerateFullPaths=true", 52 | "/consoleloggerparameters:NoSummary" 53 | ], 54 | "type": "process", 55 | "dependsOn": "clean release (functions)", 56 | "problemMatcher": "$msCompile" 57 | }, 58 | { 59 | "type": "func", 60 | "dependsOn": "build (functions)", 61 | "options": { 62 | "cwd": "${workspaceFolder}/bin/Debug/netcoreapp3.1" 63 | }, 64 | "command": "host start", 65 | "isBackground": true, 66 | "problemMatcher": "$func-dotnet-watch" 67 | } 68 | ] 69 | } -------------------------------------------------------------------------------- /advanced-vod-workflow/DemoScript.md: -------------------------------------------------------------------------------- 1 | # Publish Asset with AES Open Restriction 2 | 3 | ## Script 4 | ### 1. POST CreateContentKeyPolicy 5 | { 6 | "contentKeyPolicyName": "SharedContentKeyPolicyForClearKeyOpenRestriction", 7 | "mode": "simple", 8 | "description": "Shared token restricted policy for Clear Key content key policy", 9 | "policyOptionName": "PolicyWithClearKeyOpenRestrictionOption", 10 | "openRestriction": true, 11 | "configurationType": "ClearKey" 12 | } 13 | 14 | ### 2. POST PublishAsset 15 | { 16 | "assetName": "RollsRoyceIoT-048cf514-6337-430a-b538-94b1727dbc91", 17 | "streamingPolicyName": "Predefined_ClearKey", 18 | "contentKeyPolicyName": "SharedContentKeyPolicyForClearKeyOpenRestriction", 19 | "contentKeys": [ 20 | { 21 | "id": "271b37ed-6098-4a16-8e16-c3beb79c6199", 22 | "type": "EnvelopeEncryption", 23 | "labelReferenceInStreamingPolicy": "clearKeyDefault", 24 | "value": "on1W311b4SiZ9c2KCDIy8w==", 25 | "policyName": null, 26 | "tracks": [] 27 | } 28 | ], 29 | "startDateTime": "2020-04-20T00:00Z", 30 | "endDateTime": "2025-12-31T23:59Z" 31 | } 32 | 33 | ## Clean Up 34 | ### 1. POST UnpublishAsset 35 | { 36 | "streamingLocatorName": "streaminglocator-6bcfd46d-ee30-4c4f-bd0a-9852a00a0452" 37 | } 38 | 39 | ### 2. POST DeleteContentKeyPolicy 40 | { 41 | "contentKeyPolicyName": "SharedContentKeyPolicyForClearKeyOpenRestriction", 42 | } 43 | 44 | # Publish Asset with AES Jwt Token Restriction 45 | 46 | ## Script 47 | ### 1. POST CreateContentKeyPolicy 48 | { 49 | "contentKeyPolicyName": "SharedContentKeyPolicyForClearKey", 50 | "mode": "advanced", 51 | "description": "Shared token restricted policy for Clear Key content key policy", 52 | "contentKeyPolicyOptions": [ 53 | { 54 | "name": "PolicyWithClearKeyOption", 55 | "configuration": { 56 | "@odata.type": "#Microsoft.Media.ContentKeyPolicyClearKeyConfiguration" 57 | }, 58 | "restriction": { 59 | "@odata.type": "#Microsoft.Media.ContentKeyPolicyTokenRestriction", 60 | "issuer": "myIssuer", 61 | "audience": "myAudience", 62 | "primaryVerificationKey": { 63 | "@odata.type": "#Microsoft.Media.ContentKeyPolicySymmetricTokenKey", 64 | "keyValue": "on1W311b4SiZ9c2KCDIy8w==" 65 | }, 66 | "alternateVerificationKeys": [], 67 | "requiredClaims": [ 68 | { 69 | "claimType": "urn:microsoft:azure:mediaservices:contentkeyidentifier", 70 | "claimValue": null 71 | } 72 | ], 73 | "restrictionTokenType": "Jwt", 74 | "openIdConnectDiscoveryDocument": null 75 | } 76 | } 77 | ] 78 | } 79 | 80 | ### 2. POST PublishAsset 81 | { 82 | "assetName": "AzureMediaServices-ba6aad15-0417-44d7-b778-159b12adb990", 83 | "streamingPolicyName": "Predefined_ClearKey", 84 | "contentKeyPolicyName": "SharedContentKeyPolicyForClearKey", 85 | "contentKeys": [ 86 | { 87 | "id": "1d5f3e32-7314-46ac-a713-3e066d289ed7", 88 | "type": "EnvelopeEncryption", 89 | "labelReferenceInStreamingPolicy": "clearKeyDefault", 90 | "value": "njjOFXLDn3RLfkKTdnrwZQ==", 91 | "policyName": null, 92 | "tracks": [] 93 | } 94 | ], 95 | "startDateTime": "2020-04-20T00:00Z", 96 | "endDateTime": "2025-12-31T23:59Z" 97 | } 98 | 99 | ## Clean Up 100 | ### 1. POST UnpublishAsset 101 | { 102 | "streamingLocatorName": "streaminglocator-0705f511-879a-4258-9ce2-ca0e13c31995" 103 | } 104 | 105 | ### 2. POST DeleteContentKeyPolicy 106 | { 107 | "contentKeyPolicyName": "SharedContentKeyPolicyForClearKey" 108 | } -------------------------------------------------------------------------------- /advanced-vod-workflow/Functions/CreateEmptyAsset.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 Functions 3 | // 4 | // CreateEmptyAsset - This function creates an empty asset. 5 | // 6 | /* 7 | ```c# 8 | Input: 9 | { 10 | // [Required] The name of the asset 11 | "assetNamePrefix": "TestAssetName", 12 | 13 | // The description of the asset, 14 | "assetDescription": "Filename", 15 | 16 | // The name of attached storage account where to create the asset 17 | "assetStorageAccount": "storage01" 18 | } 19 | Output: 20 | { 21 | // The name of the asset created 22 | "assetName": "TestAssetName-180c777b-cd3c-4e02-b362-39b8d94d7a85", 23 | 24 | // The identifier of the asset created 25 | "assetId": "nb:cid:UUID:68adb036-43b7-45e6-81bd-8cf32013c810", 26 | 27 | // The name of the destination container name for the asset created 28 | "destinationContainer": "asset-4a5f429c-686c-4f6f-ae86-4078a4e6139e" 29 | } 30 | 31 | ``` 32 | */ 33 | // 34 | // 35 | 36 | using advanced_vod_functions_v3.SharedLibs; 37 | using Microsoft.AspNetCore.Http; 38 | using Microsoft.AspNetCore.Mvc; 39 | using Microsoft.Azure.Management.Media; 40 | using Microsoft.Azure.Management.Media.Models; 41 | using Microsoft.Azure.WebJobs; 42 | using Microsoft.Azure.WebJobs.Extensions.Http; 43 | using Microsoft.Extensions.Logging; 44 | using Newtonsoft.Json; 45 | using System; 46 | using System.IO; 47 | using System.Threading.Tasks; 48 | 49 | 50 | namespace advanced_vod_functions_v3 51 | { 52 | public static class CreateEmptyAsset 53 | { 54 | [FunctionName("CreateEmptyAsset")] 55 | public static async Task Run( 56 | [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, 57 | ILogger log) 58 | { 59 | log.LogInformation($"AMS v3 Function - CreateEmptyAsset was triggered!"); 60 | 61 | string requestBody = new StreamReader(req.Body).ReadToEnd(); 62 | dynamic data = JsonConvert.DeserializeObject(requestBody); 63 | 64 | if (data.assetNamePrefix == null) 65 | return new BadRequestObjectResult("Please pass assetNamePrefix in the input object"); 66 | string assetStorageAccount = null; 67 | if (data.assetStorageAccount != null) 68 | assetStorageAccount = data.assetStorageAccount; 69 | 70 | Guid assetGuid = Guid.NewGuid(); 71 | string assetName = data.assetNamePrefix + "-" + assetGuid.ToString(); 72 | 73 | string assetDescription = assetName; 74 | if (data.assetDescription != null) 75 | assetDescription = data.assetDescription; 76 | 77 | MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); 78 | Asset asset = null; 79 | 80 | try 81 | { 82 | IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); 83 | 84 | Asset assetParams = new Asset(null, assetName, null, assetGuid, DateTime.Now, DateTime.Now, null, assetDescription, null, assetStorageAccount, AssetStorageEncryptionFormat.None); 85 | asset = client.Assets.CreateOrUpdate(amsconfig.ResourceGroup, amsconfig.AccountName, assetName, assetParams); 86 | //asset = client.Assets.CreateOrUpdate(amsconfig.ResourceGroup, amsconfig.AccountName, assetName, new Asset()); 87 | } 88 | catch (ApiErrorException e) 89 | { 90 | log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); 91 | return new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message); 92 | } 93 | catch (Exception e) 94 | { 95 | log.LogError($"ERROR: Exception with message: {e.Message}"); 96 | return new BadRequestObjectResult("Error: " + e.Message); 97 | } 98 | 99 | // compatible with AMS V2 API 100 | string assetId = "nb:cid:UUID:" + asset.AssetId; 101 | string destinationContainer = "asset-" + asset.AssetId; 102 | 103 | return (ActionResult)new OkObjectResult(new 104 | { 105 | assetName = assetName, 106 | assetId = assetId, 107 | destinationContainer = destinationContainer 108 | }); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /advanced-vod-workflow/Functions/CreateJwtToken.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 Functions 3 | // 4 | // CreateJwtToken - Create a Jwt Token 5 | // 6 | /* 7 | ```c# 8 | Input: 9 | { 10 | // [Required] The name of the streaming locator 11 | "streamingLocatorName": "locator-12345", 12 | 13 | // [Required] The name of the content key policy 14 | "contentKeyPolicyName": "SharedContentKeyPolicyForClearKey", 15 | 16 | // [Required] The name of the Azure Media Service account 17 | "accountName": "amsaccount", 18 | 19 | // [Required] The resource group of the Azure Media Service account 20 | "resourceGroup": "mediaservices-rg" 21 | } 22 | Output: 23 | { 24 | // The created token 25 | "token": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1cm46bWljcm9zb2Z0OmF6dXJlOm1lZGlhc2VydmljZXM6Y29udGVudGtleWlkZW50aWZpZXIiOiJmZWQ1ZWE5Ni0wMjRhLTQ1YmMtYmE5My02MDFlZWU4MjcxNTgiLCJuYmYiOjE1ODc3MDUxOTgsImV4cCI6MTU4NzcwNTc5OCwiaXNzIjoibXlJc3N1ZXIiLCJhdWQiOiJteUF1ZGllbmNlIn0.UOLeEdiMfTKdIcr3SA-tO0nbD5AAedTKBAf815quNmI", 26 | } 27 | 28 | ``` 29 | */ 30 | // 31 | // 32 | 33 | using advanced_vod_functions_v3.SharedLibs; 34 | using Microsoft.AspNetCore.Http; 35 | using Microsoft.AspNetCore.Mvc; 36 | using Microsoft.Azure.Management.Media; 37 | using Microsoft.Azure.Management.Media.Models; 38 | using Microsoft.Azure.WebJobs; 39 | using Microsoft.Azure.WebJobs.Extensions.Http; 40 | using Microsoft.Extensions.Logging; 41 | using Microsoft.IdentityModel.Tokens; 42 | using Newtonsoft.Json; 43 | using System; 44 | using System.IdentityModel.Tokens.Jwt; 45 | using System.IO; 46 | using System.Linq; 47 | using System.Security.Claims; 48 | using System.Threading.Tasks; 49 | 50 | namespace advanced_vod_functions_v3 51 | { 52 | public static class CreateJwtToken 53 | { 54 | [FunctionName("CreateJwtToken")] 55 | public static async Task Run( 56 | [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, 57 | ILogger log) 58 | { 59 | log.LogInformation($"AMS v3 Function - CreateJwtToken was triggered!"); 60 | 61 | string requestBody = new StreamReader(req.Body).ReadToEnd(); 62 | dynamic data = JsonConvert.DeserializeObject(requestBody); 63 | 64 | string streaminglocatorName = data.streamingLocatorName; 65 | string contentKeyPolicyName = data.contentKeyPolicyName; 66 | string accountName = data.accountName; 67 | string resourceGroup = data.resourceGroup; 68 | 69 | MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); 70 | string token = null; 71 | 72 | try 73 | { 74 | IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); 75 | 76 | var response = client.StreamingLocators.ListContentKeys(resourceGroup, accountName, streaminglocatorName); 77 | var contentKeyId = response.ContentKeys.First().Id.ToString(); 78 | var policyProperties = client.ContentKeyPolicies.GetPolicyPropertiesWithSecrets(resourceGroup, accountName, contentKeyPolicyName); 79 | 80 | var ckrestriction = (ContentKeyPolicyTokenRestriction)policyProperties.Options.FirstOrDefault()?.Restriction; 81 | var symKey = (ContentKeyPolicySymmetricTokenKey)ckrestriction.PrimaryVerificationKey; 82 | var tokenSigningKey = new SymmetricSecurityKey(symKey.KeyValue); 83 | 84 | SigningCredentials cred = new SigningCredentials( 85 | tokenSigningKey, 86 | // Use the HmacSha256 and not the HmacSha256Signature option, or the token will not work! 87 | SecurityAlgorithms.HmacSha256, 88 | SecurityAlgorithms.Sha256Digest); 89 | 90 | Claim[] claims = new Claim[] { 91 | new Claim(ContentKeyPolicyTokenClaim.ContentKeyIdentifierClaim.ClaimType, contentKeyId) 92 | }; 93 | 94 | JwtSecurityToken jwtToken = new JwtSecurityToken( 95 | issuer: ckrestriction.Issuer, 96 | audience: ckrestriction.Audience, 97 | claims: claims, 98 | notBefore: DateTime.Now.AddMinutes(-5), 99 | expires: DateTime.Now.AddMinutes(60), 100 | signingCredentials: cred); 101 | 102 | JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler(); 103 | token = $"Bearer {handler.WriteToken(jwtToken)}"; 104 | } 105 | catch (ApiErrorException e) 106 | { 107 | log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); 108 | return new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message); 109 | } 110 | catch (Exception e) 111 | { 112 | log.LogError($"ERROR: Exception with message: {e.Message}"); 113 | return new BadRequestObjectResult("Error: " + e.Message); 114 | } 115 | 116 | return (ActionResult)new OkObjectResult(new 117 | { 118 | token = token 119 | }); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /advanced-vod-workflow/Functions/DeleteAsset.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 Functions 3 | // 4 | // DeleteAsset - This function deletes an existing asset. 5 | // 6 | /* 7 | ```c# 8 | Input: 9 | { 10 | // [Required] The name of the asset 11 | "assetName": "TestAssetName-180c777b-cd3c-4e02-b362-39b8d94d7a85", 12 | 13 | // [Required] The name of the Azure Media Service account 14 | "accountName": "amsaccount", 15 | 16 | // [Required] The resource group of the Azure Media Service account 17 | "resourceGroup": "mediaservices-rg" 18 | } 19 | Output: 20 | { 21 | // The name of the deleted asset 22 | "assetName": "TestAssetName-180c777b-cd3c-4e02-b362-39b8d94d7a85" 23 | } 24 | 25 | ``` 26 | */ 27 | // 28 | // 29 | 30 | using advanced_vod_functions_v3.SharedLibs; 31 | using Microsoft.AspNetCore.Http; 32 | using Microsoft.AspNetCore.Mvc; 33 | using Microsoft.Azure.Management.Media; 34 | using Microsoft.Azure.Management.Media.Models; 35 | using Microsoft.Azure.WebJobs; 36 | using Microsoft.Azure.WebJobs.Extensions.Http; 37 | using Microsoft.Extensions.Logging; 38 | using Newtonsoft.Json; 39 | using System; 40 | using System.IO; 41 | using System.Threading.Tasks; 42 | 43 | 44 | namespace advanced_vod_functions_v3 45 | { 46 | public static class DeleteAsset 47 | { 48 | [FunctionName("DeleteAsset")] 49 | public static async Task Run( 50 | [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, 51 | ILogger log) 52 | { 53 | log.LogInformation($"AMS v3 Function - DeleteAsset was triggered!"); 54 | 55 | string requestBody = new StreamReader(req.Body).ReadToEnd(); 56 | dynamic data = JsonConvert.DeserializeObject(requestBody); 57 | 58 | string assetName = data.assetName; 59 | string accountName = data.accountName; 60 | string resourceGroup = data.resourceGroup; 61 | 62 | MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); 63 | 64 | try 65 | { 66 | IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); 67 | 68 | client.Assets.Delete(resourceGroup, accountName, assetName); 69 | } 70 | catch (ApiErrorException e) 71 | { 72 | log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); 73 | return new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message); 74 | } 75 | catch (Exception e) 76 | { 77 | log.LogError($"ERROR: Exception with message: {e.Message}"); 78 | return new BadRequestObjectResult("Error: " + e.Message); 79 | } 80 | 81 | // compatible with AMS V2 API 82 | return (ActionResult)new OkObjectResult(new 83 | { 84 | assetName = assetName 85 | }); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /advanced-vod-workflow/Functions/DeleteContentKeyPolicy.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 Functions 3 | // 4 | // DeleteContentKeyPolicy - This function deletes an ContentKeyPolicy object. 5 | // 6 | /* 7 | ```c# 8 | Input: 9 | { 10 | // [Required] The content key policy name. 11 | "contentKeyPolicyName": "SharedContentKeyPolicyForClearKey", 12 | } 13 | Output: 14 | { 15 | // The name of the deleted content key policy. 16 | "contentKeyPolicyName": "SharedContentKeyPolicyForClearKey", 17 | } 18 | 19 | ``` 20 | */ 21 | // 22 | // 23 | 24 | using advanced_vod_functions_v3.SharedLibs; 25 | using Microsoft.AspNetCore.Http; 26 | using Microsoft.AspNetCore.Mvc; 27 | using Microsoft.Azure.Management.Media; 28 | using Microsoft.Azure.Management.Media.Models; 29 | using Microsoft.Azure.WebJobs; 30 | using Microsoft.Azure.WebJobs.Extensions.Http; 31 | using Microsoft.Extensions.Logging; 32 | using Newtonsoft.Json; 33 | using System; 34 | using System.IO; 35 | using System.Threading.Tasks; 36 | 37 | 38 | namespace advanced_vod_functions_v3 39 | { 40 | public static class DeleteContentKeyPolicy 41 | { 42 | [FunctionName("DeleteContentKeyPolicy")] 43 | public static async Task Run( 44 | [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, 45 | ILogger log) 46 | { 47 | log.LogInformation($"AMS v3 Function - CreateContentKeyPolicy was triggered!"); 48 | 49 | string requestBody = new StreamReader(req.Body).ReadToEnd(); 50 | dynamic data = JsonConvert.DeserializeObject(requestBody); 51 | 52 | if (data.contentKeyPolicyName == null) 53 | return new BadRequestObjectResult("Please pass contentKeyPolicyName in the input object"); 54 | string contentKeyPolicyName = data.contentKeyPolicyName; 55 | 56 | try 57 | { 58 | MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); 59 | IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); 60 | 61 | client.ContentKeyPolicies.Delete(amsconfig.ResourceGroup, amsconfig.AccountName, contentKeyPolicyName); 62 | } 63 | catch (ApiErrorException e) 64 | { 65 | log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); 66 | return new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message); 67 | } 68 | catch (Exception e) 69 | { 70 | log.LogError($"ERROR: Exception with message: {e.Message}"); 71 | return new BadRequestObjectResult("Error: " + e.Message); 72 | } 73 | 74 | return (ActionResult)new OkObjectResult(new 75 | { 76 | contentKeyPolicyName = contentKeyPolicyName 77 | }); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /advanced-vod-workflow/Functions/DeleteStreamingPolicy.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 Functions 3 | // 4 | // DeleteStreamingPolicy - This function deletes an StreamingPolicy object. 5 | // 6 | /* 7 | ```c# 8 | Input: 9 | { 10 | // [Required] The name of the streaming policy. 11 | "streamingPolicyName": "SharedStreamingForClearKey", 12 | } 13 | Output: 14 | { 15 | // The name of the streaming policy. 16 | "streamingPolicyName": "SharedStreamingForClearKey", 17 | } 18 | 19 | ``` 20 | */ 21 | // 22 | // 23 | 24 | using advanced_vod_functions_v3.SharedLibs; 25 | using Microsoft.AspNetCore.Http; 26 | using Microsoft.AspNetCore.Mvc; 27 | using Microsoft.Azure.Management.Media; 28 | using Microsoft.Azure.Management.Media.Models; 29 | using Microsoft.Azure.WebJobs; 30 | using Microsoft.Azure.WebJobs.Extensions.Http; 31 | using Microsoft.Extensions.Logging; 32 | using Newtonsoft.Json; 33 | using System; 34 | using System.IO; 35 | using System.Threading.Tasks; 36 | 37 | 38 | namespace advanced_vod_functions_v3 39 | { 40 | public static class DeleteStreamingPolicy 41 | { 42 | [FunctionName("DeleteStreamingPolicy")] 43 | public static async Task Run( 44 | [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, 45 | ILogger log) 46 | { 47 | log.LogInformation($"AMS v3 Function - CreateStreamingPolicy was triggered!"); 48 | 49 | string requestBody = new StreamReader(req.Body).ReadToEnd(); 50 | dynamic data = JsonConvert.DeserializeObject(requestBody); 51 | 52 | if (data.streamingPolicyName == null) 53 | return new BadRequestObjectResult("Please pass streamingPolicyName in the input object"); 54 | string streamingPolicyName = data.streamingPolicyName; 55 | 56 | MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); 57 | 58 | try 59 | { 60 | IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); 61 | 62 | client.StreamingPolicies.Delete(amsconfig.ResourceGroup, amsconfig.AccountName, streamingPolicyName); 63 | } 64 | catch (ApiErrorException e) 65 | { 66 | log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); 67 | return new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message); 68 | } 69 | catch (Exception e) 70 | { 71 | log.LogError($"ERROR: Exception with message: {e.Message}"); 72 | return new BadRequestObjectResult("Error: " + e.Message); 73 | } 74 | 75 | return (ActionResult)new OkObjectResult(new 76 | { 77 | streamingPolicyName = streamingPolicyName 78 | }); 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /advanced-vod-workflow/Functions/GetTransform.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 Functions 3 | // 4 | // CreateTransform - This function creates a new transform. 5 | // 6 | /* 7 | ```c# 8 | Input: 9 | { 10 | // [Required] The name of the transform. 11 | "transformName": "TestTransform" 12 | } 13 | Output: 14 | { 15 | // The name of the created TransformName 16 | "transformName": "TestTransform", 17 | 18 | // The resource identifier of the created Transform 19 | "transformId": "/subscriptions/694d5930-8ee4-4e50-917b-9dcfeceb6179/resourceGroups/AMSdemo/providers/Microsoft.Media/mediaservices/amsdemojapaneast/transforms/TestTransform" 20 | } 21 | 22 | ``` 23 | */ 24 | // 25 | // 26 | 27 | using advanced_vod_functions_v3.SharedLibs; 28 | using Microsoft.AspNetCore.Http; 29 | using Microsoft.AspNetCore.Mvc; 30 | using Microsoft.Azure.Management.Media; 31 | using Microsoft.Azure.Management.Media.Models; 32 | using Microsoft.Azure.WebJobs; 33 | using Microsoft.Azure.WebJobs.Extensions.Http; 34 | using Microsoft.Extensions.Logging; 35 | using Newtonsoft.Json; 36 | using System; 37 | using System.IO; 38 | using System.Threading.Tasks; 39 | 40 | 41 | namespace advanced_vod_functions_v3 42 | { 43 | public static class GetTransform 44 | { 45 | [FunctionName("GetTransform")] 46 | public static async Task Run( 47 | [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, 48 | ILogger log) 49 | { 50 | log.LogInformation($"AMS v3 Function - GetTransform was triggered!"); 51 | 52 | string requestBody = new StreamReader(req.Body).ReadToEnd(); 53 | dynamic data = JsonConvert.DeserializeObject(requestBody); 54 | 55 | if (data.transformName == null) 56 | return new BadRequestObjectResult("Please pass transformName in the input object"); 57 | string transformName = data.transformName; 58 | 59 | MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); 60 | string transformId = null; 61 | 62 | try 63 | { 64 | IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); 65 | 66 | Transform transform = client.Transforms.Get(amsconfig.ResourceGroup, amsconfig.AccountName, transformName); 67 | if (transform == null) 68 | transformName = null; 69 | else 70 | transformId = transform.Id; 71 | } 72 | catch (ApiErrorException e) 73 | { 74 | log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); 75 | return new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message); 76 | } 77 | catch (Exception e) 78 | { 79 | log.LogError($"ERROR: Exception with message: {e.Message}"); 80 | return new BadRequestObjectResult("Error: " + e.Message); 81 | } 82 | 83 | return (ActionResult)new OkObjectResult(new 84 | { 85 | transformName = transformName, 86 | transformId = transformId 87 | }); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /advanced-vod-workflow/Functions/MonitorMediaJob.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 Functions 3 | // 4 | // MonitorMediaJob - This function monitors media job. 5 | // 6 | /* 7 | ```c# 8 | Input: 9 | { 10 | // [Required] The name of the media job 11 | "jobName": "amsv3function-job-24369d2e-7415-4ff5-ba12-b8a879a15401", 12 | 13 | // [Required] The name of the Transform for the media job 14 | "transformName": "TestTransform" 15 | } 16 | Output: 17 | { 18 | // The status name of the media job 19 | "jobStatus": "Finished", 20 | 21 | // The status of each task/output asset in the media job 22 | "jobOutputStateList": [ 23 | { 24 | // Name of the Output Asset 25 | "AssetName": "out-testasset-efbf71e8-3f80-480d-9b92-f02bef6ad4d2", 26 | // Status of the media task for the Output Asset 27 | "State": "Finished", 28 | // Progress of the media task for the Output Asset 29 | "Progress": 100 30 | }, 31 | ... 32 | ] 33 | } 34 | 35 | ``` 36 | */ 37 | // // https://docs.microsoft.com/en-us/dotnet/api/microsoft.windowsazure.mediaservices.client.jobstate?view=azure-dotnet 38 | // // Queued 0 39 | // // Scheduled 1 40 | // // Processing 2 41 | // // Finished 3 42 | // // Error 4 43 | // // Canceled 5 44 | // // Canceling 6 45 | // 46 | // 47 | 48 | using advanced_vod_functions_v3.SharedLibs; 49 | using Microsoft.AspNetCore.Http; 50 | using Microsoft.AspNetCore.Mvc; 51 | using Microsoft.Azure.Management.Media; 52 | using Microsoft.Azure.Management.Media.Models; 53 | using Microsoft.Azure.WebJobs; 54 | using Microsoft.Azure.WebJobs.Extensions.Http; 55 | using Microsoft.Extensions.Logging; 56 | using Newtonsoft.Json; 57 | using Newtonsoft.Json.Linq; 58 | using System; 59 | using System.IO; 60 | using System.Threading.Tasks; 61 | 62 | 63 | namespace advanced_vod_functions_v3 64 | { 65 | public static class MonitorMediaJob 66 | { 67 | [FunctionName("MonitorMediaJob")] 68 | public static async Task Run( 69 | [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, 70 | ILogger log) 71 | { 72 | log.LogInformation($"AMS v3 Function - MonitorMediaJob was triggered!"); 73 | 74 | string requestBody = new StreamReader(req.Body).ReadToEnd(); 75 | dynamic data = JsonConvert.DeserializeObject(requestBody); 76 | 77 | // Validate input objects 78 | if (data.jobName == null) 79 | return new BadRequestObjectResult("Please pass jobName in the input object"); 80 | if (data.transformName == null) 81 | return new BadRequestObjectResult("Please pass transformName in the input object"); 82 | string jobName = data.jobName; 83 | string transformName = data.transformName; 84 | 85 | 86 | MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); 87 | Job job = null; 88 | 89 | try 90 | { 91 | IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); 92 | job = client.Jobs.Get(amsconfig.ResourceGroup, amsconfig.AccountName, transformName, jobName); 93 | 94 | } 95 | catch (ApiErrorException e) 96 | { 97 | log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); 98 | return new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message); 99 | } 100 | catch (Exception e) 101 | { 102 | log.LogError($"ERROR: Exception with message: {e.Message}"); 103 | return new BadRequestObjectResult("Error: " + e.Message); 104 | } 105 | 106 | JObject result = new JObject(); 107 | result["jobStatus"] = job.State.ToString(); 108 | JArray jobOutputStateList = new JArray(); 109 | foreach (JobOutputAsset o in job.Outputs) 110 | { 111 | JObject jobOutputState = new JObject(); 112 | jobOutputState["AssetName"] = o.AssetName; 113 | jobOutputState["State"] = o.State.ToString(); 114 | jobOutputState["Progress"] = o.Progress; 115 | if (o.Error != null) 116 | { 117 | jobOutputState["ErrorCode"] = o.Error.Code.ToString(); 118 | jobOutputState["ErrorMessage"] = o.Error.Message.ToString(); 119 | } 120 | jobOutputStateList.Add(jobOutputState); 121 | } 122 | result["jobOutputStateList"] = jobOutputStateList; 123 | 124 | return (ActionResult)new OkObjectResult(result); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /advanced-vod-workflow/Functions/UnpublishAsset.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 Functions 3 | // 4 | // UnpublishAsset - This function unpublishes an asset by deleting its streaming locator and policy. 5 | // 6 | /* 7 | ```c# 8 | Input: 9 | { 10 | // [Required] The name of the streaming locator 11 | "streamingLocatorName": "streaminglocator-911b65de-ac92-4391-9aab-80021126d403" 12 | } 13 | Output: 14 | { 15 | // The name of the deleted StreamingLocatorName 16 | "streamingLocatorName": "streaminglocator-911b65de-ac92-4391-9aab-80021126d403" 17 | 18 | // The name of the deleted streamingPolicyName 19 | "streamingPolicyName": "SharedStreamingForClearKey" 20 | } 21 | 22 | ``` 23 | */ 24 | // 25 | // 26 | 27 | using advanced_vod_functions_v3.SharedLibs; 28 | using Microsoft.AspNetCore.Http; 29 | using Microsoft.AspNetCore.Mvc; 30 | using Microsoft.Azure.Management.Media; 31 | using Microsoft.Azure.Management.Media.Models; 32 | using Microsoft.Azure.WebJobs; 33 | using Microsoft.Azure.WebJobs.Extensions.Http; 34 | using Microsoft.Extensions.Logging; 35 | using Newtonsoft.Json; 36 | using System; 37 | using System.IO; 38 | using System.Threading.Tasks; 39 | 40 | 41 | namespace advanced_vod_functions_v3 42 | { 43 | public static class UnpublishAsset 44 | { 45 | [FunctionName("UnpublishAsset")] 46 | public static async Task Run( 47 | [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, 48 | ILogger log) 49 | { 50 | log.LogInformation($"AMS v3 Function - PublishAsset was triggered!"); 51 | 52 | string requestBody = new StreamReader(req.Body).ReadToEnd(); 53 | dynamic data = JsonConvert.DeserializeObject(requestBody); 54 | 55 | // Validate input objects 56 | if (data.streamingLocatorName == null) 57 | return new BadRequestObjectResult("Please pass streamingLocatorName in the input object"); 58 | string streamingLocatorName = data.streamingLocatorName; 59 | string streamingPolicyName = null; 60 | 61 | MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); 62 | string contentKeysJson = string.Empty; 63 | try 64 | { 65 | IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); 66 | 67 | var streamingLocator = client.StreamingLocators.Get(amsconfig.ResourceGroup, amsconfig.AccountName, streamingLocatorName); 68 | streamingPolicyName = streamingLocator.StreamingPolicyName; 69 | 70 | client.StreamingLocators.Delete(amsconfig.ResourceGroup, amsconfig.AccountName, streamingLocatorName); 71 | if (!streamingPolicyName.StartsWith("Predefined_")) 72 | client.StreamingPolicies.Delete(amsconfig.ResourceGroup, amsconfig.AccountName, streamingPolicyName); 73 | } 74 | catch (ApiErrorException e) 75 | { 76 | log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); 77 | return new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message); 78 | } 79 | catch (Exception e) 80 | { 81 | log.LogError($"ERROR: Exception with message: {e.Message}"); 82 | return new BadRequestObjectResult("Error: " + e.Message); 83 | } 84 | 85 | return (ActionResult)new OkObjectResult(new 86 | { 87 | streamingLocatorName = streamingLocatorName, 88 | streamingPolicyName = streamingPolicyName == null || streamingPolicyName.StartsWith("Predefined_") ? "None" : streamingPolicyName, 89 | contentKeys = contentKeysJson 90 | }); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /advanced-vod-workflow/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | services: media-services,functions,logic-app 3 | platforms: dotnetcore 4 | author: shigeyf 5 | --- 6 | 7 | # Media Services: Integrating Azure Media Services with Azure Functions & Azure Logic Apps 8 | 9 | This project contains advanced VOD media workflow examples of using Azure Functions with Azure Media Services. 10 | The project includes several folders of sample Azure Functions for use with Azure Media Services that show workflows related to ingesting content directly from blob storage, encoding, and writing content back to blob storage. 11 | 12 | This Media Services Functions example is based on AMS REST API v3 on Azure Functions v3. 13 | 14 | ## Prerequisites for a sample Logic Apps deployments 15 | 16 | ### 1. Create an Azure Media Services account 17 | 18 | Create a Media Services account in your subscription if don't have it already ([follow this article](https://docs.microsoft.com/en-us/azure/media-services/latest/create-account-howto?tabs=portal)). 19 | 20 | ### 2. Create a Service Principal 21 | 22 | Create a Service Principal and save the password. It will be needed in step #4. To do so, go to the API tab in the account ([follow this article](https://docs.microsoft.com/en-us/azure/media-services/latest/access-api-howto?tabs=portal)). 23 | 24 | ### 3. Make sure the AMS streaming endpoint is started 25 | 26 | To enable streaming, go to the Azure portal, select the Azure Media Services account which has been created, and start the default streaming endpoint. 27 | 28 | ### 4. Deploy the Azure functions 29 | 30 | If not already done : fork the repo, deploy Azure Functions and select the **"advanced-vod-workflow-functions"** Project (IMPORTANT!) 31 | 32 | Follow the guidelines in the [git tutorial](1-CONTRIBUTION-GUIDE/git-tutorial.md) for details on how to fork the project and use Git properly with this project. 33 | 34 | Note : if you never provided your GitHub account in the Azure portal before, the continous integration probably will probably fail and you won't see the functions. In that case, you need to setup it manually. Go to your azure functions deployment / Functions app settings / Configure continous integration. Select GitHub as a source and configure it to use your fork. 35 | 36 | 37 | 38 | Alternatively, deploy the [Azure Functions using Visual Studio](https://docs.microsoft.com/en-us/azure/azure-functions/functions-develop-vs#publish-to-azure). 39 | 40 | ## Functions documentation 41 | 42 | This [page](Functions-documentation.md) lists the functions available and describes the input and output parameters. 43 | -------------------------------------------------------------------------------- /advanced-vod-workflow/SharedLibs/BlobStorageHelper.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 - Functions 3 | // 4 | // Shared Library 5 | // 6 | 7 | using Microsoft.WindowsAzure.Storage; 8 | using Microsoft.WindowsAzure.Storage.Auth; 9 | using Microsoft.WindowsAzure.Storage.Blob; 10 | using System; 11 | using System.Collections.Generic; 12 | 13 | 14 | namespace advanced_vod_functions_v3.SharedLibs 15 | { 16 | public class BlobStorageHelper 17 | { 18 | static public CloudBlobContainer GetCloudBlobContainer(string storageAccountName, string storageAccountKey, string containerName) 19 | { 20 | CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(storageAccountName, storageAccountKey), true); 21 | CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient(); 22 | return cloudBlobClient.GetContainerReference(containerName); 23 | } 24 | 25 | static public List ListBlobs(CloudBlobContainer blobContainer) 26 | { 27 | List blobList = new List(); 28 | string blobPrefix = null; 29 | bool useFlatBlobListing = true; 30 | BlobContinuationToken blobContinuationToken = null; 31 | 32 | do 33 | { 34 | var results = blobContainer.ListBlobsSegmentedAsync(blobPrefix, useFlatBlobListing, BlobListingDetails.Copy, null, blobContinuationToken, null, null).Result; 35 | 36 | // Get the value of the continuation token returned by the listing call. 37 | blobContinuationToken = results.ContinuationToken; 38 | 39 | foreach (IListBlobItem item in results.Results) 40 | { 41 | if (item.GetType() == typeof(CloudBlockBlob)) 42 | { 43 | CloudBlockBlob blob = (CloudBlockBlob)item; 44 | blobList.Add(blob); 45 | } 46 | } 47 | } while (blobContinuationToken != null); // Loop while the continuation token is not null. 48 | 49 | return blobList; 50 | } 51 | 52 | static public async void CopyBlobsAsync(CloudBlobContainer sourceBlobContainer, CloudBlobContainer destinationBlobContainer, List fileNames) 53 | { 54 | if (fileNames != null) 55 | { 56 | foreach (var fileName in fileNames) 57 | { 58 | CloudBlob sourceBlob = sourceBlobContainer.GetBlockBlobReference(fileName); 59 | CloudBlob destinationBlob = destinationBlobContainer.GetBlockBlobReference(fileName); 60 | CopyBlobAsync(sourceBlob as CloudBlob, destinationBlob); 61 | } 62 | } 63 | else 64 | { 65 | string blobPrefix = null; 66 | BlobContinuationToken blobContinuationToken = null; 67 | do 68 | { 69 | var results = await sourceBlobContainer.ListBlobsSegmentedAsync(blobPrefix, blobContinuationToken); 70 | // Get the value of the continuation token returned by the listing call. 71 | blobContinuationToken = results.ContinuationToken; 72 | foreach (IListBlobItem item in results.Results) 73 | { 74 | if (item.GetType() == typeof(CloudBlockBlob)) 75 | { 76 | CloudBlockBlob sourceBlob = (CloudBlockBlob)item; 77 | CloudBlob destinationBlob = destinationBlobContainer.GetBlockBlobReference(sourceBlob.Name); 78 | CopyBlobAsync(sourceBlob as CloudBlob, destinationBlob); 79 | } 80 | } 81 | } while (blobContinuationToken != null); // Loop while the continuation token is not null. 82 | } 83 | } 84 | 85 | static public async void CopyBlobAsync(CloudBlob sourceBlob, CloudBlob destinationBlob) 86 | { 87 | var signature = sourceBlob.GetSharedAccessSignature(new SharedAccessBlobPolicy 88 | { 89 | Permissions = SharedAccessBlobPermissions.Read, 90 | SharedAccessExpiryTime = DateTime.UtcNow.AddHours(24) 91 | }); 92 | await destinationBlob.StartCopyAsync(new Uri(sourceBlob.Uri.AbsoluteUri + signature)); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /advanced-vod-workflow/SharedLibs/GenericHelper.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 - Functions 3 | // 4 | // Shared Library 5 | // 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | 10 | 11 | namespace advanced_vod_functions_v3.SharedLibs 12 | { 13 | class GenericHelper 14 | { 15 | public static byte[] GetRandomBuffer(int length) 16 | { 17 | var returnValue = new byte[length]; 18 | 19 | using (var rng = new System.Security.Cryptography.RNGCryptoServiceProvider()) 20 | { 21 | rng.GetBytes(returnValue); 22 | } 23 | 24 | return returnValue; 25 | } 26 | } 27 | public class JsonObjectConversionRule 28 | { 29 | public Type t; 30 | public Dictionary rules; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /advanced-vod-workflow/SharedLibs/MediaServiceClientCredentials.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 - Functions 3 | // 4 | // Shared Library 5 | // 6 | 7 | using System; 8 | 9 | namespace advanced_vod_functions_v3.SharedLibs 10 | { 11 | public class MediaServiceClientCredentials 12 | { 13 | public Uri AadEndpoint 14 | { 15 | get { return new Uri(Environment.GetEnvironmentVariable("AadEndpoint")); } 16 | } 17 | 18 | public string AadTenantId 19 | { 20 | get { return Environment.GetEnvironmentVariable("AadTenantId"); } 21 | } 22 | 23 | public string AadClientId 24 | { 25 | get { return Environment.GetEnvironmentVariable("AadClientId"); } 26 | } 27 | 28 | public string AadClientSecret 29 | { 30 | get { return Environment.GetEnvironmentVariable("AadClientSecret"); } 31 | } 32 | 33 | public Uri ArmEndpoint 34 | { 35 | get { return new Uri(Environment.GetEnvironmentVariable("ArmEndpoint")); } 36 | } 37 | 38 | public Uri ArmAadAudience 39 | { 40 | get { return new Uri(Environment.GetEnvironmentVariable("ArmAadAudience")); } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /advanced-vod-workflow/SharedLibs/MediaServicesConfigWrapper.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 - Functions 3 | // 4 | // Shared Library 5 | // 6 | 7 | using System; 8 | 9 | namespace advanced_vod_functions_v3.SharedLibs 10 | { 11 | public class MediaServicesConfigWrapper 12 | { 13 | public MediaServiceClientCredentials mediaServiceClientCredentials = new MediaServiceClientCredentials(); 14 | 15 | public string SubscriptionId 16 | { 17 | get { return Environment.GetEnvironmentVariable("SubscriptionId"); } 18 | } 19 | 20 | public string ResourceGroup 21 | { 22 | get { return Environment.GetEnvironmentVariable("ResourceGroup"); } 23 | } 24 | 25 | public string AccountName 26 | { 27 | get { return Environment.GetEnvironmentVariable("AccountName"); } 28 | } 29 | 30 | public string Region 31 | { 32 | get { return Environment.GetEnvironmentVariable("Region"); } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /advanced-vod-workflow/SharedLibs/MediaServicesHelper.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Azure Media Services REST API v3 - Functions 3 | // 4 | // Shared Library 5 | // 6 | 7 | using Microsoft.Azure.Management.Media; 8 | using Microsoft.Azure.Management.Media.Models; 9 | using Microsoft.IdentityModel.Clients.ActiveDirectory; 10 | using Microsoft.Rest; 11 | using Microsoft.Rest.Azure.Authentication; 12 | using Newtonsoft.Json.Linq; 13 | using System.Collections.Generic; 14 | using System.Threading.Tasks; 15 | 16 | 17 | namespace advanced_vod_functions_v3.SharedLibs 18 | { 19 | public class MediaServicesHelper 20 | { 21 | public static IAzureMediaServicesClient CreateMediaServicesClientAsync(MediaServicesConfigWrapper config) 22 | { 23 | var credentials = GetCredentialsAsync(config).Result; 24 | 25 | return new AzureMediaServicesClient(config.mediaServiceClientCredentials.ArmEndpoint, credentials) 26 | { 27 | SubscriptionId = config.SubscriptionId, 28 | }; 29 | } 30 | 31 | private static async Task GetCredentialsAsync(MediaServicesConfigWrapper config) 32 | { 33 | // Use ApplicationTokenProvider.LoginSilentAsync to get a token using a service principal with symetric key 34 | ClientCredential clientCredential = new ClientCredential(config.mediaServiceClientCredentials.AadClientId, config.mediaServiceClientCredentials.AadClientSecret); 35 | return await ApplicationTokenProvider.LoginSilentAsync(config.mediaServiceClientCredentials.AadTenantId, clientCredential, ActiveDirectoryServiceSettings.Azure); 36 | } 37 | 38 | static public PublishAssetOutput ConvertToPublishAssetOutput(string locatorName, string streamingUrlPrefx, ListPathsResponse paths) 39 | { 40 | PublishAssetOutput output = new PublishAssetOutput(); 41 | 42 | output.locatorName = locatorName; 43 | output.streamingUrl = ""; 44 | output.captionVttUrl = ""; 45 | output.annotationsJsonUrl = ""; 46 | output.contentModerationJsonUrl = ""; 47 | output.facesJsonUrl = ""; 48 | output.insightsJsonUrl = ""; 49 | output.ocrJsonUrl = ""; 50 | 51 | List psUrls = new List(); 52 | foreach (var path in paths.StreamingPaths) 53 | { 54 | PublishStreamingUrls s = new PublishStreamingUrls(); 55 | s.streamingProtocol = path.StreamingProtocol.ToString(); 56 | s.encryptionScheme = path.EncryptionScheme.ToString(); 57 | s.urls = new string[path.Paths.Count]; 58 | for (int i = 0; i < path.Paths.Count; i++) s.urls[i] = "https://" + streamingUrlPrefx + path.Paths[i]; 59 | if (path.StreamingProtocol.ToString() == "SmoothStreaming") 60 | output.streamingUrl = "https://" + streamingUrlPrefx + path.Paths[0]; 61 | psUrls.Add(s); 62 | } 63 | output.streamingUrls = psUrls.ToArray(); 64 | 65 | List dUrls = new List(); 66 | foreach (var path in paths.DownloadPaths) 67 | { 68 | dUrls.Add("https://" + streamingUrlPrefx + path); 69 | if (path.EndsWith("annotations.json")) output.annotationsJsonUrl = "https://" + streamingUrlPrefx + path; 70 | if (path.EndsWith("contentmoderation.json")) output.contentModerationJsonUrl = "https://" + streamingUrlPrefx + path; 71 | if (path.EndsWith("faces.json")) output.facesJsonUrl = "https://" + streamingUrlPrefx + path; 72 | if (path.EndsWith("insights.json")) output.insightsJsonUrl = "https://" + streamingUrlPrefx + path; 73 | if (path.EndsWith("transcript.vtt")) output.captionVttUrl = "https://" + streamingUrlPrefx + path; 74 | } 75 | output.downloadUrls = dUrls.ToArray(); 76 | 77 | return output; 78 | } 79 | } 80 | 81 | public class PublishStreamingUrls 82 | { 83 | public string streamingProtocol; 84 | public string encryptionScheme; 85 | public string[] urls; 86 | } 87 | 88 | public class PublishAssetOutput 89 | { 90 | public string locatorName; 91 | public string streamingUrl; 92 | // Audio Analyzer - VTT (speech-to-text) 93 | public string captionVttUrl; 94 | // Video Analyzer JSON 95 | public string annotationsJsonUrl; 96 | public string contentModerationJsonUrl; 97 | public string facesJsonUrl; 98 | public string insightsJsonUrl; 99 | public string ocrJsonUrl; 100 | // URLs 101 | public PublishStreamingUrls[] streamingUrls; 102 | public string[] downloadUrls; 103 | } 104 | 105 | public static class JsonExtensions 106 | { 107 | public static List FindTokens(this JToken containerToken, string name) 108 | { 109 | List matches = new List(); 110 | FindTokens(containerToken, name, matches); 111 | return matches; 112 | } 113 | 114 | private static void FindTokens(JToken containerToken, string name, List matches) 115 | { 116 | if (containerToken.Type == JTokenType.Object) 117 | { 118 | foreach (JProperty child in containerToken.Children()) 119 | { 120 | if (child.Name == name) 121 | { 122 | matches.Add(child.Value); 123 | } 124 | FindTokens(child.Value, name, matches); 125 | } 126 | } 127 | else if (containerToken.Type == JTokenType.Array) 128 | { 129 | foreach (JToken child in containerToken.Children()) 130 | { 131 | FindTokens(child, name, matches); 132 | } 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /advanced-vod-workflow/advanced_vod_functions_v3.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp3.1 4 | v3 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | PreserveNewest 24 | 25 | 26 | PreserveNewest 27 | Never 28 | 29 | 30 | -------------------------------------------------------------------------------- /advanced-vod-workflow/advanced_vod_functions_v3.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30011.22 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "advanced_vod_functions_v3", "advanced_vod_functions_v3.csproj", "{EF247D55-CF5C-4A45-A13B-CF3BEE2B46FF}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {EF247D55-CF5C-4A45-A13B-CF3BEE2B46FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {EF247D55-CF5C-4A45-A13B-CF3BEE2B46FF}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {EF247D55-CF5C-4A45-A13B-CF3BEE2B46FF}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {EF247D55-CF5C-4A45-A13B-CF3BEE2B46FF}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {4A3D7932-8484-4DA4-B7C1-A5B69714768F} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /advanced-vod-workflow/host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0", 3 | "logging": { 4 | "applicationInsights": { 5 | "samplingExcludedTypes": "Request", 6 | "samplingSettings": { 7 | "isEnabled": true 8 | } 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /advanced-vod-workflow/local.settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "AzureWebJobsStorage": "UseDevelopmentStorage=true", 5 | "FUNCTIONS_WORKER_RUNTIME": "dotnet" 6 | } 7 | } -------------------------------------------------------------------------------- /azuredeploy.parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "appName": { 6 | "value": "GEN-UNIQUE" 7 | }, 8 | "functionKey": { 9 | "value" : "GEN-UNIQUE" 10 | }, 11 | "sourceCodeRepositoryURL": { 12 | "value": "ENTER PATH TO YOUR FORK OF - https://github.com/Azure-Samples/media-services-dotnet-functions-integration" 13 | }, 14 | "sourceCodeBranch": { 15 | "value": "main" 16 | }, 17 | "sourceCodeManualIntegration": { 18 | "value": true 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /logic-app-using-workflow-functions/.gitignore: -------------------------------------------------------------------------------- 1 | *.local.json -------------------------------------------------------------------------------- /logic-app-using-workflow-functions/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | services: media-services,logic-app 3 | platforms: dotnetcore 4 | author: raffertyuy 5 | --- 6 | 7 | # Media Services: Integrating Azure Media Services with Azure Functions & Azure Logic Apps 8 | 9 | This project contains Logic Apps that converts-to-media-asset, encodes and publishes media files that you upload in an Azure Storage. 10 | 11 | The project includes Logic Apps ARM templates that is dependent on the [advanced-vod-workflow](../advanced-vod-workflow) functions. There are 2 projects, one for uploading and encoding and another for publishing. 12 | 13 | This Media Services Functions example is based on AMS REST API v3 on Azure Functions v3. 14 | 15 | See [here](https://raffertyuy.com/raztype/creating-an-azure-media-services-upload-workflow-using-azure-storage-and-logic-apps) to read more about how this logic app is implemented. 16 | 17 | ## Prerequisites for a sample Logic Apps deployments 18 | 19 | ### 1. Deploy the Advanced VOD Workflow Azure Functions 20 | 21 | Follow the instructions and deploy the azure functions found in [advanced-vod-workflow](../advanced-vod-workflow). 22 | 23 | ### 2. Edit the ARM Template Parameters 24 | 25 | Update the parameters found in the LogicApp.json of both projects 26 | 27 | ### 3. Deploy the 2 Logic App Projects 28 | 29 | See [here](https://docs.microsoft.com/en-us/azure/logic-apps/logic-apps-azure-resource-manager-templates-overview) for instructions. 30 | -------------------------------------------------------------------------------- /logic-app-using-workflow-functions/publish-logicapp/Deployment.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | bin\$(Configuration)\ 7 | false 8 | true 9 | false 10 | None 11 | obj\ 12 | $(BaseIntermediateOutputPath)\ 13 | $(BaseIntermediateOutputPath)$(Configuration)\ 14 | $(IntermediateOutputPath)ProjectReferences 15 | $(ProjectReferencesOutputPath)\ 16 | true 17 | 18 | 19 | 20 | false 21 | false 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Always 33 | 34 | 35 | Never 36 | 37 | 38 | false 39 | Build 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | _GetDeploymentProjectContent; 48 | _CalculateContentOutputRelativePaths; 49 | _GetReferencedProjectsOutput; 50 | _CalculateArtifactStagingDirectory; 51 | _CopyOutputToArtifactStagingDirectory; 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Configuration=$(Configuration);Platform=$(Platform) 69 | 70 | 71 | 75 | 76 | 77 | 78 | $([System.IO.Path]::GetFileNameWithoutExtension('%(ProjectReference.Identity)')) 79 | 80 | 81 | 82 | 83 | 84 | 85 | $(OutDir) 86 | $(OutputPath) 87 | $(ArtifactStagingDirectory)\ 88 | $(ArtifactStagingDirectory)staging\ 89 | $(Build_StagingDirectory) 90 | 91 | 92 | 93 | 94 | 96 | 97 | <_OriginalIdentity>%(DeploymentProjectContentOutput.Identity) 98 | <_RelativePath>$(_OriginalIdentity.Replace('$(MSBuildProjectDirectory)', '')) 99 | 100 | 101 | 102 | 103 | $(_RelativePath) 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | PrepareForRun 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /logic-app-using-workflow-functions/publish-logicapp/LogicApp.parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "LogicAppName": { 6 | "value": "raz-mediaservices-publishworkflow-logic" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /logic-app-using-workflow-functions/publish-logicapp/publish-logicapp.deployproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 8 | 9 | Release 10 | AnyCPU 11 | 12 | 13 | 14 | 1d3b4467-6b5a-49e9-a1de-4fbe59b2184b 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | False 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /logic-app-using-workflow-functions/publish-streaming.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30114.105 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{151D2E53-A2C4-4D7D-83FE-D05416EBD58E}") = "publish-logicapp", "publish-logicapp\publish-logicapp.deployproj", "{1D3B4467-6B5A-49E9-A1DE-4FBE59B2184B}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {1D3B4467-6B5A-49E9-A1DE-4FBE59B2184B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {1D3B4467-6B5A-49E9-A1DE-4FBE59B2184B}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {1D3B4467-6B5A-49E9-A1DE-4FBE59B2184B}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {1D3B4467-6B5A-49E9-A1DE-4FBE59B2184B}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {D3EBB541-0775-4B6E-A972-498EDA852FD3} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /logic-app-using-workflow-functions/upload-and-encode-logicapp/Deployment.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | bin\$(Configuration)\ 7 | false 8 | true 9 | false 10 | None 11 | obj\ 12 | $(BaseIntermediateOutputPath)\ 13 | $(BaseIntermediateOutputPath)$(Configuration)\ 14 | $(IntermediateOutputPath)ProjectReferences 15 | $(ProjectReferencesOutputPath)\ 16 | true 17 | 18 | 19 | 20 | false 21 | false 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Always 33 | 34 | 35 | Never 36 | 37 | 38 | false 39 | Build 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | _GetDeploymentProjectContent; 48 | _CalculateContentOutputRelativePaths; 49 | _GetReferencedProjectsOutput; 50 | _CalculateArtifactStagingDirectory; 51 | _CopyOutputToArtifactStagingDirectory; 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Configuration=$(Configuration);Platform=$(Platform) 69 | 70 | 71 | 75 | 76 | 77 | 78 | $([System.IO.Path]::GetFileNameWithoutExtension('%(ProjectReference.Identity)')) 79 | 80 | 81 | 82 | 83 | 84 | 85 | $(OutDir) 86 | $(OutputPath) 87 | $(ArtifactStagingDirectory)\ 88 | $(ArtifactStagingDirectory)staging\ 89 | $(Build_StagingDirectory) 90 | 91 | 92 | 93 | 94 | 96 | 97 | <_OriginalIdentity>%(DeploymentProjectContentOutput.Identity) 98 | <_RelativePath>$(_OriginalIdentity.Replace('$(MSBuildProjectDirectory)', '')) 99 | 100 | 101 | 102 | 103 | $(_RelativePath) 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | PrepareForRun 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /logic-app-using-workflow-functions/upload-and-encode-logicapp/LogicApp.parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "videoindexer-v2_1_Connection_Name": { 6 | "value": "videoindexer-v2" 7 | }, 8 | "azureblob_1_accessKey": { 9 | "value": "" 10 | }, 11 | "videoindexer-v2_1_api_key": { 12 | "value": "" 13 | }, 14 | "keyvault_1_token:clientId": { 15 | "value": "" 16 | }, 17 | "keyvault_1_token:clientSecret": { 18 | "value": "" 19 | }, 20 | "keyvault_1_token:TenantId": { 21 | "value": "" 22 | }, 23 | "keyvault_1_token:resourceUri": { 24 | "value": "" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /logic-app-using-workflow-functions/upload-and-encode-logicapp/upload-and-encode-logicapp.deployproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 8 | 9 | Release 10 | AnyCPU 11 | 12 | 13 | 14 | fe66cf17-0477-415a-8014-dc18097f41ff 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | False 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /logic-app-using-workflow-functions/upload-and-encode.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30114.105 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{151D2E53-A2C4-4D7D-83FE-D05416EBD58E}") = "upload-and-encode-logicapp", "upload-and-encode-logicapp\upload-and-encode-logicapp.deployproj", "{FE66CF17-0477-415A-8014-DC18097F41FF}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {FE66CF17-0477-415A-8014-DC18097F41FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {FE66CF17-0477-415A-8014-DC18097F41FF}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {FE66CF17-0477-415A-8014-DC18097F41FF}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {FE66CF17-0477-415A-8014-DC18097F41FF}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {D3EBB541-0775-4B6E-A972-498EDA852FD3} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /sample.env: -------------------------------------------------------------------------------- 1 | # Copy the content of this file to a file named ".env". It should be stored at the root of the repo. 2 | # Make sure to exclude this file from GitHub checkins using a .gitignore file 3 | 4 | # The values can be obtained from the API Access page for your Media Services account in the portal. 5 | AZURE_TENANT_DOMAIN="microsoft.onmicrosoft.com" 6 | AZURE_TENANT_ID="00000000-0000-0000-0000-000000000000" 7 | AZURE_MEDIA_SERVICES_ACCOUNT_NAME="amsaccount" 8 | AZURE_RESOURCE_GROUP="amsResourceGroup" 9 | AZURE_SUBSCRIPTION_ID="00000000-0000-0000-0000-000000000000" 10 | AZURE_ARM_TOKEN_AUDIENCE="https://management.core.windows.net/" 11 | AZURE_ARM_ENDPOINT="https://management.azure.com/" 12 | 13 | # You must change this if you are using Gov Cloud, China, or other non standard cloud regions 14 | AZURE_AAD_ENDPOINT="https://login.microsoftonline.com" --------------------------------------------------------------------------------