Skip to content

Commit 3c64aee

Browse files
author
singhkays
committed
adding azure functions
1 parent e97013b commit 3c64aee

File tree

4 files changed

+190
-0
lines changed

4 files changed

+190
-0
lines changed

functions/ffmpeg-concat/__init__.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import logging
2+
import json
3+
4+
import azure.functions as func
5+
6+
# This function is used to create a concat file as per the ffmpeg concat demuxer schema
7+
# https://trac.ffmpeg.org/wiki/Concatenate#demuxer
8+
9+
10+
def main(req: func.HttpRequest) -> func.HttpResponse:
11+
logging.info('Python HTTP trigger function processed a request.')
12+
body = req.get_body()
13+
aciShotsVideoPath = req.headers.get('aciShotsVideoPath')
14+
15+
# Fail if no request body passed
16+
if not body:
17+
return func.HttpResponse(
18+
"Please pass in the request body",
19+
status_code=400
20+
)
21+
22+
if not (aciShotsVideoPath):
23+
return func.HttpResponse(
24+
"Please pass required parameters in the request body",
25+
status_code=400
26+
)
27+
28+
shots = json.loads(body)
29+
returnString = ''
30+
31+
# We're creating a concat file for ffmpeg demuxer
32+
# See example schema here https://trac.ffmpeg.org/wiki/Concatenate#demuxer
33+
34+
for shot in shots:
35+
# If we're at last shot, then don't add a new line
36+
if shot == shots[-1]:
37+
returnString += "file '{}{}.mkv'".format(aciShotsVideoPath, shot['id'])
38+
else:
39+
returnString += "file '{}{}.mkv'\n".format(aciShotsVideoPath, shot['id'])
40+
41+
return func.HttpResponse(
42+
returnString,
43+
status_code=200
44+
)

functions/ffmpeg-concat/function.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"scriptFile": "__init__.py",
3+
"bindings": [
4+
{
5+
"authLevel": "function",
6+
"type": "httpTrigger",
7+
"direction": "in",
8+
"name": "req",
9+
"methods": [
10+
"get",
11+
"post"
12+
]
13+
},
14+
{
15+
"type": "http",
16+
"direction": "out",
17+
"name": "$return"
18+
}
19+
]
20+
}

functions/parse/__init__.py

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import logging
2+
import json
3+
import copy
4+
5+
import azure.functions as func
6+
7+
# JSON schema to create container instances with the Logic App connector
8+
# We'll fill in the values here using this azure function and define all the container instances to be created programattically
9+
aciStringSchema = """
10+
{
11+
"name": "1",
12+
"properties": {
13+
"image": "",
14+
"resources": {
15+
"requests": {
16+
"memoryInGB": 1,
17+
"cpu": 4
18+
}
19+
},
20+
"command": [
21+
""
22+
],
23+
"volumeMounts": [
24+
{
25+
"name": "",
26+
"mountPath": "",
27+
"readOnly": false
28+
},
29+
{
30+
"name": "",
31+
"mountPath": "",
32+
"readOnly": false
33+
}
34+
]
35+
}
36+
}
37+
"""
38+
39+
def main(req: func.HttpRequest) -> func.HttpResponse:
40+
logging.info('Python HTTP trigger function processed a request.')
41+
body = req.get_body()
42+
43+
# Fail if no request body passed
44+
if not body:
45+
return func.HttpResponse(
46+
"Please pass in the request body",
47+
status_code=400
48+
)
49+
50+
shots = json.loads(body)
51+
aciJsonSchema = json.loads(aciStringSchema)
52+
53+
# Array of conatiner instances JSON definition objects that we'll return to the ACI Logic App connector
54+
returnArray = []
55+
56+
# Get header parameters that are passed in from Logic App ACI connector
57+
videoName = req.headers.get('videoName')
58+
ffmpegBinaryPath = req.headers.get('ffmpegBinaryPath')
59+
60+
aciSourceVideoMountName = req.headers.get('aciSourceVideoMountName')
61+
aciSourceVideoPath = req.headers.get('aciSourceVideoPath')
62+
63+
aciDestinationVideoMountName = req.headers.get('aciDestinationVideoMountName')
64+
aciDestinationVideoPath = req.headers.get('aciDestinationVideoPath')
65+
66+
dockerImage = req.headers.get('dockerImage')
67+
requestedMemoryInGB = float(req.headers.get('requestedMemoryInGB'))
68+
requestedCPUCores = int(req.headers.get('requestedCPUCores'))
69+
70+
if not (videoName or ffmpegBinaryPath or aciSourceVideoMountName or aciSourceVideoPath or aciDestinationVideoMountName or aciDestinationVideoPath or dockerImage or requestedMemoryInGB or requestedCPUCores):
71+
# Fail if any of the parameters are missing
72+
return func.HttpResponse(
73+
"Please pass required parameters in the request body",
74+
status_code=400
75+
)
76+
77+
for shot in shots:
78+
containerJson = copy.deepcopy(aciJsonSchema)
79+
80+
# ffmpeg command that needs to be run by the container instance to encode the video
81+
# Using ffmpeg -ss and -t parameters, we'll only encode a specific time chunk
82+
# The time chunks that we need to encode correspond to the different scene timestamps that we'll get from the video index
83+
# To fix the ffmpeg error - "Too many packets buffered for output stream 0:1"
84+
# Added -max_muxing_queue_size 1024
85+
ffmpegCommand = "{}/ffmpeg -i {}{} -v debug -c:v libaom-av1 -crf 22 -b:v 0 -strict experimental -cpu-used 4 -row-mt 1 -tiles 2x1 -c:a copy -max_muxing_queue_size 102400 -ss {} -t {} -y {}{}.mkv"
86+
formattedFFmpeg = ffmpegCommand.format(ffmpegBinaryPath, aciSourceVideoPath, videoName, shot['instances'][0]['start'], shot['instances'][0]['duration'], aciDestinationVideoPath, shot['id'])
87+
aciCommand = formattedFFmpeg.split(' ')
88+
89+
containerJson['name'] = 'shot-' + "{}".format(shot['id'])
90+
containerJson['properties']['image'] = dockerImage
91+
containerJson['properties']['resources']['requests']['memoryInGB'] = requestedMemoryInGB
92+
containerJson['properties']['resources']['requests']['cpu'] = requestedCPUCores
93+
containerJson['properties']['command'] = aciCommand
94+
containerJson['properties']['volumeMounts'][0]['name'] = aciSourceVideoMountName
95+
containerJson['properties']['volumeMounts'][0]['mountPath'] = aciSourceVideoPath
96+
containerJson['properties']['volumeMounts'][1]['name'] = aciDestinationVideoMountName
97+
containerJson['properties']['volumeMounts'][1]['mountPath'] = aciDestinationVideoPath
98+
returnArray.append(containerJson)
99+
100+
# For testing the returnArray
101+
#print(returnArray)
102+
103+
return func.HttpResponse(
104+
json.dumps(returnArray),
105+
status_code=200
106+
)

functions/parse/function.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"scriptFile": "__init__.py",
3+
"bindings": [
4+
{
5+
"authLevel": "anonymous",
6+
"type": "httpTrigger",
7+
"direction": "in",
8+
"name": "req",
9+
"methods": [
10+
"get",
11+
"post"
12+
]
13+
},
14+
{
15+
"type": "http",
16+
"direction": "out",
17+
"name": "$return"
18+
}
19+
]
20+
}

0 commit comments

Comments
 (0)