Skip to content

Commit 4028e1b

Browse files
authored
Merge pull request #55 from shigeyf/master
Added Video Analyzer for Content Moderator modules.
2 parents 8c55b93 + 147cec1 commit 4028e1b

File tree

1 file changed

+279
-0
lines changed

1 file changed

+279
-0
lines changed
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
from os import getcwd, makedirs, path
2+
# from urllib import parse # Python 3.x
3+
from urlparse import urlparse # Python 2.x
4+
import uuid
5+
import datetime
6+
import time
7+
8+
import adal
9+
from msrestazure.azure_active_directory import AdalAuthentication
10+
from msrestazure.azure_cloud import AZURE_PUBLIC_CLOUD
11+
from azure.mgmt.media import AzureMediaServices
12+
from azure.mgmt.media.models import *
13+
from azure.storage.blob import BlockBlobService
14+
15+
16+
def video_analyze():
17+
"""VideoAnalyze.
18+
19+
This will analyze a video and download insights for video analysis
20+
"""
21+
# Your configurations for your AMS account
22+
account_name = 'ams account'
23+
resource_group_name = 'ResourceGroup'
24+
subscription_id = '00000000-0000-0000-0000-000000000000'
25+
aad_client_id = '00000000-0000-0000-0000-000000000000'
26+
aad_secret = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
27+
tenant_id = 'tenant domain or tenant id'
28+
29+
# Your input file name and output folder name for encoding
30+
input_mp4_file_name = 'ignite.mp4'
31+
input_mp4_files_baseurl = 'https://shigeyfampdemo.azurewebsites.net/videos/'
32+
input_mp4_files = ['ignite.mp4']
33+
output_folder_name = 'output'
34+
transform_name = 'MyVideoAnalyzerTransformName'
35+
36+
# The Azure Media Services (AMS) Client
37+
# You can now use this object to perform different operations to your AMS account.
38+
login_endpoint = AZURE_PUBLIC_CLOUD.endpoints.active_directory
39+
resource = AZURE_PUBLIC_CLOUD.endpoints.active_directory_resource_id
40+
context = adal.AuthenticationContext(login_endpoint + '/' + tenant_id)
41+
credentials = AdalAuthentication(
42+
context.acquire_token_with_client_credentials,
43+
resource,
44+
aad_client_id,
45+
aad_secret
46+
)
47+
client = AzureMediaServices(credentials, subscription_id)
48+
49+
# Creating a unique suffix so that we don't have name collisions if you run the sample
50+
# multiple times without cleaning up.
51+
uniqueness = str(uuid.uuid1())
52+
job_name = 'job-{}'.format(uniqueness)
53+
output_asset_name = 'output-{}'.format(uniqueness)
54+
input_asset_name = 'input-{}'.format(uniqueness)
55+
56+
# Ensure that you have the desired video analyzer Transform. This is really a one time setup operation.
57+
get_or_create_transform(client, resource_group_name, account_name, transform_name, VideoAnalyzerPreset(audio_language = 'en-US'))
58+
59+
# Create a new job input
60+
# Option 1) Create a new job input with Asset and upload the specified local video file into it.
61+
#create_input_asset(client, resource_group_name, account_name, input_asset_name, input_mp4_file_name)
62+
#job_input = JobInputAsset(asset_name = inputasset_name)
63+
# Option 2) Create a new job input with HTTP
64+
job_input = JobInputHttp(base_uri = input_mp4_files_baseurl, files = input_mp4_files)
65+
66+
# Output from the encoding Job must be written to an Asset, so let's create one
67+
output_asset = create_output_asset(client, resource_group_name, account_name, output_asset_name)
68+
69+
job = submit_job(client, resource_group_name, account_name, transform_name, job_name, job_input, output_asset.name)
70+
71+
# In this demo code, we will poll for Job status
72+
# Polling is not a recommended best practice for production applications because of the latency it introduces.
73+
# Overuse of this API may trigger throttling. Developers should instead use Event Grid.
74+
job = wait_for_job_to_finish(client, resource_group_name, account_name, transform_name, job_name)
75+
76+
if job.state == JobState.finished:
77+
print('Job finished.')
78+
download_output_asset(client, resource_group_name, account_name, output_asset.name, output_folder_name)
79+
print('Done.')
80+
81+
82+
def get_or_create_transform(client, resource_group_name, account_name, transform_name, preset):
83+
"""If the specified transform exists, get that transform.
84+
If the it does not exist, creates a new transform with the specified output.
85+
In this case, the output is set to encode a video using one of the built-in encoding presets.
86+
:param client: The Azure Media Services Client.
87+
:param resource_group_name: The name of the resource group within the Azure subscription.
88+
:param account_name: The Media Services account name.
89+
:param transform_name: The transform name.
90+
:param preset: the preset.
91+
:return Transform
92+
:rtype: ~azure.mgmt.media.models.Transform
93+
"""
94+
# Does a Transform already exist with the desired name? Assume that an existing Transform with the desired name
95+
# also uses the same recipe or Preset for processing content.
96+
transform = client.transforms.get(resource_group_name, account_name, transform_name)
97+
98+
if not transform:
99+
transformOutput = TransformOutput(preset = preset)
100+
# You need to specify what you want it to produce as an output
101+
output = [transformOutput]
102+
# Create the Transform with the output defined above
103+
transform = client.transforms.create_or_update(resource_group_name, account_name, transform_name, output)
104+
105+
return transform
106+
107+
def create_input_asset(client, resource_group_name, account_name, asset_name, file_to_upload):
108+
""" Creates a new input Asset and uploads the specified local video file into it.
109+
:param client: The Azure Media Services Client.
110+
:param resource_group_name: The name of the resource group within the Azure subscription.
111+
:param account_name: The Media Services account name.
112+
:param asset_name: The asset name.
113+
:param file_to_upload: The file you want to upload into the asset.
114+
:return Asset
115+
:rtype: ~azure.mgmt.media.models.Asset
116+
"""
117+
# In this example, we are assuming that the asset name is unique.
118+
#
119+
# If you already have an asset with the desired name, use the Assets.Get method
120+
# to get the existing asset. In Media Services v3, the Get method on entities returns null
121+
# if the entity doesn't exist (a case-insensitive check on the name).
122+
123+
# Call Media Services API to create an Asset.
124+
# This method creates a container in storage for the Asset.
125+
# The files (blobs) associated with the asset will be stored in this container.
126+
asset = client.assets.create_or_update(resource_group_name, account_name, asset_name, Asset())
127+
128+
# Use Media Services API to get back a response that contains
129+
# SAS URL for the Asset container into which to upload blobs.
130+
# That is where you would specify read-write permissions
131+
# and the expiration time for the SAS URL.
132+
# expiryTime = datetime.datetime.utcnow() + datetime.timedelta(hours = 4)
133+
response = client.assets.list_container_sas(
134+
resource_group_name,
135+
account_name,
136+
asset_name,
137+
permissions = AssetContainerPermission.read_write,
138+
expiry_time= datetime.datetime.utcnow() + datetime.timedelta(hours = 4))
139+
140+
sasUri = response.asset_container_sas_urls[0]
141+
142+
# Use Storage API to get a reference to the Asset container
143+
# that was created by calling Asset's CreateOrUpdate method.
144+
#parsed_url = parse.urlparse(sasUri) # Python 3.x
145+
parsed_url = urlparse(sasUri) # Python 2.x
146+
storage_account_name = parsed_url.netloc.split('.')[0]
147+
148+
# Remove the leading /
149+
container_name = parsed_url.path[1:]
150+
token = parsed_url.query
151+
local_path = getcwd()
152+
block_blob_service = BlockBlobService(account_name = storage_account_name, sas_token = token)
153+
154+
# Use Storage API to upload the file into the container in storage.
155+
block_blob_service.create_blob_from_path(container_name, file_to_upload, path.join(local_path, file_to_upload))
156+
157+
return asset
158+
159+
def create_output_asset(client, resource_group_name, account_name, asset_name):
160+
""" Creates an output asset. The output from the encoding Job must be written to an Asset.
161+
:param client: The Azure Media Services Client.
162+
:param resource_group_name: The name of the resource group within the Azure subscription.
163+
:param account_name: The Media Services account name.
164+
:param asset_name: The asset name.
165+
:return Asset
166+
:rtype: ~azure.mgmt.media.models.Asset
167+
"""
168+
# Check if an Asset already exists
169+
output_asset = client.assets.get(resource_group_name, account_name, asset_name)
170+
asset = Asset()
171+
output_asset_name = asset_name
172+
173+
if output_asset is not None:
174+
# Name collision! In order to get the sample to work, let's just go ahead and create a unique asset name
175+
# Note that the returned Asset can have a different name than the one specified as an input parameter.
176+
# You may want to update this part to throw an Exception instead, and handle name collisions differently.
177+
uniqueness = uuid.uuid1
178+
output_asset_name += str(uniqueness)
179+
print('Warning - found an existing Asset with name = ' + asset_name)
180+
print('Creating an Asset with this name instead: ' + output_asset_name)
181+
182+
return client.assets.create_or_update(resource_group_name, account_name, output_asset_name, asset)
183+
184+
def submit_job(client, resource_group_name, account_name, transform_name, job_name, job_input, outputasset_name):
185+
"""Submits a request to Media Services to apply the specified Transform to a given input video.
186+
:param client: The Azure Media Services Client.
187+
:param resource_group_name: The name of the resource group within the Azure subscription.
188+
:param account_name: The Media Services account name.
189+
:param transform_name: The transform name.
190+
:param job_name: The job name.
191+
:param inputasset_name: The inputAsset name.
192+
:param outputasset_name: The outputAsset name.
193+
:return: Job
194+
:rtype: ~azure.mgmt.media.models.Job
195+
"""
196+
job_output = JobOutputAsset(asset_name = outputasset_name)
197+
job_outputs = [job_output]
198+
job = Job(input = job_input, outputs = job_outputs)
199+
200+
# In this example, we are assuming that the job name is unique.
201+
#
202+
# If you already have a job with the desired name, use the Jobs.Get method
203+
# to get the existing job. In Media Services v3, the Get method on entities returns null
204+
# if the entity doesn't exist (a case-insensitive check on the name).
205+
job = client.jobs.create(resource_group_name, account_name, transform_name, job_name, job)
206+
207+
return job
208+
209+
def wait_for_job_to_finish(client, resource_group_name, account_name, transform_name, job_name):
210+
""" Polls Media Services for the status of the Job.
211+
:param client: The Azure Media Services Client.
212+
:param resource_group_name: The name of the resource group within the Azure subscription.
213+
:param account_name: The Media Services account name.
214+
:param transform_name: The transform name.
215+
:param job_name: The job name.
216+
:return: Job
217+
:rtype: ~azure.mgmt.media.models.Job
218+
"""
219+
SleepIntervalMs = 5
220+
221+
while True:
222+
job = client.jobs.get(resource_group_name, account_name, transform_name, job_name)
223+
print('Job is {}'.format(job.state))
224+
225+
for i in range(len(job.outputs)):
226+
output = job.outputs[i]
227+
print(' JobOutput[{}] is {}'.format(i, output.state))
228+
if output.state == JobState.processing:
229+
print(' Progress: {}'.format(output.progress))
230+
231+
if job.state == JobState.finished or job.state == JobState.error or job.state == JobState.canceled:
232+
break
233+
234+
time.sleep(SleepIntervalMs)
235+
236+
return job
237+
238+
def download_output_asset(client, resource_group_name, account_name, asset_name, output_folder_name):
239+
"""Downloads the results from the specified output asset, so you can see what you got.
240+
:param client: The Azure Media Services Client.
241+
:param resource_group_name: The name of the resource group within the Azure subscription.
242+
:param account_name: The Media Services account name.
243+
:param asset_name: The asset name.
244+
:param output_folder_name: The output folder name.
245+
"""
246+
247+
if not path.exists(output_folder_name):
248+
makedirs(output_folder_name)
249+
250+
assetContainerSas = client.assets.list_container_sas(
251+
resource_group_name,
252+
account_name,
253+
asset_name,
254+
permissions = AssetContainerPermission.read,
255+
expiry_time= datetime.datetime.utcnow() + datetime.timedelta(hours = 4))
256+
container_sas_url = assetContainerSas.asset_container_sas_urls[0]
257+
258+
directory = path.join(output_folder_name, asset_name)
259+
makedirs(directory)
260+
261+
print('Downloading output results to {}'.format(directory))
262+
263+
#parsed_url = parse.urlparse(container_sas_url) # Python 3.x
264+
parsed_url = urlparse(container_sas_url) # Python 2.x
265+
storage_account_name = parsed_url.netloc.split('.')[0]
266+
container_name = parsed_url.path[1:]
267+
token = parsed_url.query
268+
block_blob_service = BlockBlobService(account_name = storage_account_name, sas_token = token)
269+
270+
# Download the blobs in the container.
271+
generator = block_blob_service.list_blobs(container_name)
272+
for blob in generator:
273+
print('Downloading Blob: {}'.format(blob.name))
274+
block_blob_service.get_blob_to_path(container_name, blob.name, path.join(directory, blob.name))
275+
276+
print('Download complete.')
277+
278+
if __name__ == '__main__':
279+
video_analyze()

0 commit comments

Comments
 (0)