-
Notifications
You must be signed in to change notification settings - Fork 95
Jessie/video watermarking #259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 14 commits
550cc8c
f9c3817
0f93b66
b965d7b
e9916db
2b75311
813af03
3a96f04
d4d5d30
1b7deb7
668652c
aae1023
25fd1d9
c478c91
d6c4227
27b14d6
ace2335
ad3023d
52f30c0
4efff4d
f8577e7
dfaa14a
7f4f6c9
1653b7c
f5e7ab7
aa3483f
adf54a5
67772e2
8f02b66
ae61e4b
e06985c
037f6c3
377d949
8dd8a6e
fa7e76e
51713f4
e0cfbdc
de15075
f534a53
3006879
7057465
44c8bcb
e53cfde
d224ddc
921f321
dd840d1
4fca4aa
26dfcf4
fad77da
e995e6a
7e0d13f
942f5a1
6bc1dd7
460ea1f
de41ab6
ded520f
5c85980
c5782dd
2b52ced
e5cb20c
6488d6d
55c4ac4
b97b7a5
1998b6b
2cbd768
074d4b7
efced9c
bc48b5e
e154ba0
d9ed506
28a3790
ec0db71
16c943a
bff660d
703a05d
f5093a8
0bf9990
6b00cdf
f6aa71b
327eb44
284c6c8
5766b50
67b988b
83c5309
70a7b36
d808208
791c881
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "timeout": 60, | ||
| "memory": 512, | ||
| "languages": ["python"], | ||
| "modules": ["storage"] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| #!/bin/bash | ||
|
|
||
| # Arguments required by SeBS, even if unused | ||
| DIR=$1 | ||
| VERBOSE=$2 | ||
| TARGET_ARCHITECTURE=$3 | ||
|
|
||
| # This benchmark does not need any special init step. | ||
| # All dependencies (torch, opencv-python, etc.) are installed via requirements.txt. | ||
| exit 0 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import glob, os | ||
|
|
||
| def buckets_count(): | ||
| return (1, 1) | ||
|
|
||
| ''' | ||
| Generate test, small and large workload for thumbnailer. | ||
|
|
||
| :param data_dir: directory where benchmark data is placed | ||
| :param size: workload size | ||
| :param input_buckets: input storage containers for this benchmark | ||
| :param output_buckets: | ||
| :param upload_func: upload function taking three params(bucket_idx, key, filepath) | ||
| ''' | ||
| def generate_input(data_dir, size, benchmarks_bucket, input_paths, output_paths, upload_func, nosql_func): | ||
| for file in glob.glob(os.path.join(data_dir, '*.mp4')): | ||
| img = os.path.relpath(file, data_dir) | ||
| upload_func(0, img, file) | ||
| #TODO: multiple datasets | ||
| input_config = {'object': {}, 'bucket': {}} | ||
| input_config['object']['key'] = img | ||
| # The function supports only "gpu-filter" at the moment. | ||
| input_config['object']['op'] = 'gpu-filter' | ||
| input_config['object']['duration'] = 1 | ||
| input_config['bucket']['bucket'] = benchmarks_bucket | ||
| input_config['bucket']['input'] = input_paths[0] | ||
| input_config['bucket']['output'] = output_paths[0] | ||
| return input_config | ||
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,197 @@ | ||||||||||||||||||||||||||
| #!/usr/bin/env python | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| import datetime | ||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||
| import subprocess # not strictly needed anymore, but you can remove it | ||||||||||||||||||||||||||
| import stat # can also be removed if you drop ffmpeg entirely | ||||||||||||||||||||||||||
| from typing import Dict, Any | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| import numpy as np | ||||||||||||||||||||||||||
| import cv2 | ||||||||||||||||||||||||||
| import torch | ||||||||||||||||||||||||||
| import torch.nn as nn | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| from . import storage | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| client = storage.storage.get_instance() | ||||||||||||||||||||||||||
| SCRIPT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__))) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| def gpu_video_filter(video_path: str, duration: float, event: Dict[str, Any]) -> str: | ||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||
| Decode a video on CPU (OpenCV), run a heavy GPU filter with PyTorch, | ||||||||||||||||||||||||||
| and re-encode the processed video. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| This gives you a realistic FaaS workload: | ||||||||||||||||||||||||||
| - I/O via storage | ||||||||||||||||||||||||||
| - CPU video decode/encode | ||||||||||||||||||||||||||
| - GPU-heavy tensor processing | ||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| cap = cv2.VideoCapture(video_path) | ||||||||||||||||||||||||||
| if not cap.isOpened(): | ||||||||||||||||||||||||||
| raise RuntimeError(f"Could not open input video: {video_path}") | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| fps = cap.get(cv2.CAP_PROP_FPS) | ||||||||||||||||||||||||||
| if not fps or fps <= 0: | ||||||||||||||||||||||||||
| fps = 25.0 # fallback | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| max_frames = int(fps * duration) | ||||||||||||||||||||||||||
| frames = [] | ||||||||||||||||||||||||||
|
Comment on lines
+34
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard against NaN/invalid FPS values before computing If You can defensively handle this: +import math
...
- fps = cap.get(cv2.CAP_PROP_FPS)
- if not fps or fps <= 0:
+ fps = cap.get(cv2.CAP_PROP_FPS)
+ if not fps or fps <= 0 or math.isnan(fps):
fps = 25.0 # fallbackThis keeps the simple fallback while avoiding a crash on malformed metadata. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| for i in range(max_frames): | ||||||||||||||||||||||||||
| ret, frame_bgr = cap.read() | ||||||||||||||||||||||||||
| if not ret: | ||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||
| # Convert BGR (OpenCV default) to RGB | ||||||||||||||||||||||||||
| frame_rgb = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB) | ||||||||||||||||||||||||||
| frames.append(frame_rgb) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| cap.release() | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if not frames: | ||||||||||||||||||||||||||
| raise RuntimeError("No frames decoded from video (empty or too short?)") | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # Stack into (T, H, W, C) | ||||||||||||||||||||||||||
| video_np = np.stack(frames, axis=0) # uint8, 0–255 | ||||||||||||||||||||||||||
| T, H, W, C = video_np.shape | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # Convert to torch tensor: (T, C, H, W), float32 in [0, 1] | ||||||||||||||||||||||||||
| video = torch.from_numpy(video_np).permute(0, 3, 1, 2).float() / 255.0 | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | ||||||||||||||||||||||||||
| video = video.to(device) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # Simple heavy-ish GPU workload: repeated 3x3 conv + ReLU | ||||||||||||||||||||||||||
| # You can tweak num_channels, num_iters, etc. via the event | ||||||||||||||||||||||||||
| num_iters = event.get("object", {}).get("num_iters", 10) | ||||||||||||||||||||||||||
| num_channels = 3 # keep 3 so we can write back as RGB | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| conv = nn.Conv2d( | ||||||||||||||||||||||||||
| in_channels=num_channels, | ||||||||||||||||||||||||||
| out_channels=num_channels, | ||||||||||||||||||||||||||
| kernel_size=3, | ||||||||||||||||||||||||||
| padding=1, | ||||||||||||||||||||||||||
| bias=False, | ||||||||||||||||||||||||||
| ).to(device) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| with torch.no_grad(): | ||||||||||||||||||||||||||
| for _ in range(num_iters): | ||||||||||||||||||||||||||
| video = torch.relu(conv(video)) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # Back to uint8 on CPU: (T, H, W, C) | ||||||||||||||||||||||||||
| video = (video.clamp(0.0, 1.0) * 255.0).byte() | ||||||||||||||||||||||||||
| video_np_out = video.permute(0, 2, 3, 1).cpu().numpy() | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # Encode processed video with OpenCV (CPU) | ||||||||||||||||||||||||||
| base = os.path.splitext(os.path.basename(video_path))[0] | ||||||||||||||||||||||||||
| out_path = f"/tmp/processed-{base}.mp4" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| fourcc = cv2.VideoWriter_fourcc(*"mp4v") | ||||||||||||||||||||||||||
|
Comment on lines
+85
to
+89
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Harden Two related issues here:
A minimal hardening that preserves current naming: - base = os.path.splitext(os.path.basename(video_path))[0]
- out_path = f"/tmp/processed-{base}.mp4"
+ base = os.path.splitext(os.path.basename(video_path))[0]
+ out_path = os.path.join("/tmp", f"processed-{base}.mp4")
...
- download_path = f"/tmp/{key}"
+ # Restrict local path to a basename to avoid directory traversal and
+ # missing parent directories under /tmp.
+ local_name = os.path.basename(key)
+ download_path = os.path.join("/tmp", local_name)If you want to fully satisfy stricter security tooling, consider using Also applies to: 150-177 🧰 Tools🪛 Ruff (0.14.5)88-88: Probable insecure usage of temporary file or directory: "/tmp/processed-" (S108) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| writer = cv2.VideoWriter(out_path, fourcc, fps, (W, H)) | ||||||||||||||||||||||||||
| if not writer.isOpened(): | ||||||||||||||||||||||||||
| raise RuntimeError(f"Could not open VideoWriter for: {out_path}") | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| for frame_rgb in video_np_out: | ||||||||||||||||||||||||||
| frame_bgr = cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2BGR) | ||||||||||||||||||||||||||
| writer.write(frame_bgr) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| writer.release() | ||||||||||||||||||||||||||
| return out_path | ||||||||||||||||||||||||||
|
Comment on lines
+85
to
+99
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Insecure /tmp path construction remains unresolved. As flagged in the previous review, constructing predictable At minimum, use base = os.path.splitext(os.path.basename(video_path))[0]
- out_path = f"/tmp/processed-{base}.mp4"
+ out_path = os.path.join("/tmp", f"processed-{base}.mp4")For better security, consider using import tempfile
...
with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False, dir="/tmp") as tmp:
out_path = tmp.name🧰 Tools🪛 Ruff (0.14.8)87-87: Probable insecure usage of temporary file or directory: "/tmp/processed-" (S108) 92-92: Avoid specifying long messages outside the exception class (TRY003) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # You can still support multiple ops if you want in the future. | ||||||||||||||||||||||||||
| # For now, we map "gpu-filter" (or "transcode" if you want to reuse the old name) | ||||||||||||||||||||||||||
| operations = { | ||||||||||||||||||||||||||
| "gpu-filter": gpu_video_filter, | ||||||||||||||||||||||||||
| # If you want to keep old names: | ||||||||||||||||||||||||||
| # "transcode": gpu_video_filter, | ||||||||||||||||||||||||||
| # "watermark": gpu_video_filter, | ||||||||||||||||||||||||||
| # "extract-gif": gpu_video_filter, | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| def handler(event: Dict[str, Any]): | ||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||
| FaaS entrypoint. | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Expected event structure (SeBS-style): | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||
| "bucket": { | ||||||||||||||||||||||||||
| "bucket": "<bucket-name>", | ||||||||||||||||||||||||||
| "input": "<input-prefix>", | ||||||||||||||||||||||||||
| "output": "<output-prefix>" | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| "object": { | ||||||||||||||||||||||||||
| "key": "<object-key>", | ||||||||||||||||||||||||||
| "duration": <seconds>, | ||||||||||||||||||||||||||
| "op": "gpu-filter", | ||||||||||||||||||||||||||
| // optional: | ||||||||||||||||||||||||||
| // "num_iters": 20 | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| bucket = event.get("bucket", {}).get("bucket") | ||||||||||||||||||||||||||
| input_prefix = event.get("bucket", {}).get("input") | ||||||||||||||||||||||||||
| output_prefix = event.get("bucket", {}).get("output") | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| obj = event.get("object", {}) | ||||||||||||||||||||||||||
| key = obj.get("key") | ||||||||||||||||||||||||||
| duration = obj.get("duration", 5) # default: 5 seconds | ||||||||||||||||||||||||||
| op = obj.get("op", "gpu-filter") | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if op not in operations: | ||||||||||||||||||||||||||
| raise ValueError( | ||||||||||||||||||||||||||
| f"Unknown operation '{op}'. Supported: {', '.join(operations.keys())}" | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| download_path = f"/tmp/{key}" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # If you no longer ship ffmpeg/ffmpeg, you can remove this chmod block completely. | ||||||||||||||||||||||||||
| # Leaving it here is harmless if the file doesn't exist (it will just fail and pass). | ||||||||||||||||||||||||||
| ffmpeg_binary = os.path.join(SCRIPT_DIR, "ffmpeg", "ffmpeg") | ||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||
| st = os.stat(ffmpeg_binary) | ||||||||||||||||||||||||||
| os.chmod(ffmpeg_binary, st.st_mode | stat.S_IEXEC) | ||||||||||||||||||||||||||
| except OSError: | ||||||||||||||||||||||||||
| # Ignore if ffmpeg is not present or filesystem is read-only. | ||||||||||||||||||||||||||
| pass | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # --- Download phase --- | ||||||||||||||||||||||||||
| download_begin = datetime.datetime.now() | ||||||||||||||||||||||||||
| client.download(bucket, os.path.join(input_prefix, key), download_path) | ||||||||||||||||||||||||||
| download_size = os.path.getsize(download_path) | ||||||||||||||||||||||||||
| download_stop = datetime.datetime.now() | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # --- Compute phase (GPU via PyTorch) --- | ||||||||||||||||||||||||||
| process_begin = datetime.datetime.now() | ||||||||||||||||||||||||||
| upload_path = operations[op](download_path, duration, event) | ||||||||||||||||||||||||||
| process_end = datetime.datetime.now() | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # --- Upload phase --- | ||||||||||||||||||||||||||
| upload_begin = datetime.datetime.now() | ||||||||||||||||||||||||||
| filename = os.path.basename(upload_path) | ||||||||||||||||||||||||||
| upload_size = os.path.getsize(upload_path) | ||||||||||||||||||||||||||
| upload_key = client.upload(bucket, os.path.join(output_prefix, filename), upload_path) | ||||||||||||||||||||||||||
| upload_stop = datetime.datetime.now() | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| # Convert timedeltas to microseconds | ||||||||||||||||||||||||||
| download_time = (download_stop - download_begin) / datetime.timedelta(microseconds=1) | ||||||||||||||||||||||||||
| upload_time = (upload_stop - upload_begin) / datetime.timedelta(microseconds=1) | ||||||||||||||||||||||||||
| process_time = (process_end - process_begin) / datetime.timedelta(microseconds=1) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||
| "result": { | ||||||||||||||||||||||||||
| "bucket": bucket, | ||||||||||||||||||||||||||
| "key": upload_key, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| "measurement": { | ||||||||||||||||||||||||||
| "download_time": download_time, | ||||||||||||||||||||||||||
| "download_size": download_size, | ||||||||||||||||||||||||||
| "upload_time": upload_time, | ||||||||||||||||||||||||||
| "upload_size": upload_size, | ||||||||||||||||||||||||||
| "compute_time": process_time, | ||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,3 @@ | ||||||||||||||
| torch | ||||||||||||||
| opencv-python-headless | ||||||||||||||
| numpy | ||||||||||||||
|
Comment on lines
+1
to
+3
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chainPin dependency versions for reproducibility and security. The requirements file lacks version constraints, which poses reproducibility and security risks. Unpinned versions can produce non-deterministic benchmark results and may introduce breaking changes across CI/CD runs. Additionally, Consider applying version pins like this: -torch
-opencv-python-headless
-numpy
+torch==2.0.1
+opencv-python-headless==4.8.1.78
+numpy==1.24.3Once you've determined the appropriate versions, please verify:
Length of output: 2594 🏁 Script executed: #!/bin/bash
# Search for GPU, CUDA, cuDNN references in benchmark directory and related docs
echo "=== Searching for GPU/CUDA references in benchmark context ==="
rg -i "cuda|gpu|cudnn" benchmarks/200.multimedia/220.video-processing-gpu/ --type-list | head -5
rg -i "cuda|gpu|cudnn" benchmarks/200.multimedia/220.video-processing-gpu/ -A2 -B2 2>/dev/null | head -40
echo -e "\n=== Looking for setup documentation ==="
find benchmarks/200.multimedia/220.video-processing-gpu/ -type f \( -name "*.md" -o -name "*.txt" -o -name "setup*" -o -name "README*" \) Length of output: 4364 🏁 Script executed: #!/bin/bash
# Check structure of the benchmark directory and look for related docs
echo "=== Contents of video-processing-gpu benchmark directory ==="
ls -la benchmarks/200.multimedia/220.video-processing-gpu/ 2>/dev/null || echo "Directory check"
find benchmarks/200.multimedia/220.video-processing-gpu/ -type f | head -20
echo -e "\n=== Parent directory context ==="
ls -la benchmarks/200.multimedia/Length of output: 1214 🏁 Script executed: #!/bin/bash
# Search for any documentation about dependencies or torch in the benchmark context
echo "=== Searching for torch, dependencies, or installation docs ==="
rg -i "torch|dependencies|install|setup" benchmarks/200.multimedia/ --type md --type txt -A2 | head -50
echo -e "\n=== Checking if there's a main setup guide ==="
find benchmarks/ -maxdepth 2 -name "README*" -o -name "setup*" -o -name "INSTALL*" | head -10
cat benchmarks/README.md 2>/dev/null | head -30Length of output: 371 🌐 Web query: 💡 Result: Short answer: Yes — torch 2.0.1 works with NumPy 1.24, and opencv-python-headless 4.8.1 also supports NumPy 1.24. The main incompatibility to avoid is NumPy 2.x (PyTorch wheels built against NumPy 1.x can fail under NumPy 2.x). Test by importing torch and cv2 after installing. Recommended pip pins:
References:
Sources: 🌐 Web query: 💡 Result: Summary — what you need for PyTorch GPU + relevant CUDA environment variables
Sources
References (If you want, tell me your OS, PyTorch version and how you installed it and I’ll give the exact install / env var commands and a short checklist to get torch.cuda.is_available() → True.) Pin dependency versions to prevent NumPy 2.x incompatibility and ensure reproducibility. Version pinning is missing, which creates reproducibility risks. Most critically, NumPy 2.x is incompatible with PyTorch wheels built against NumPy 1.x, leading to runtime failures if versions drift across CI/CD runs. Regarding GPU support: the code correctly uses Consider pinning versions with NumPy <2.0 constraint: -torch
-opencv-python-headless
-numpy
+torch==2.0.1
+opencv-python-headless==4.8.1.78
+numpy<2.0Verify compatibility with your target environment after applying pins. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,27 +1,43 @@ | ||
| import glob, os | ||
| import glob | ||
| import os | ||
|
|
||
| def buckets_count(): | ||
| # one input bucket, one output bucket | ||
| return (1, 1) | ||
|
|
||
| ''' | ||
| Generate test, small and large workload for thumbnailer. | ||
| Generate test, small and large workload for the GPU video filter benchmark. | ||
|
|
||
| :param data_dir: directory where benchmark data is placed | ||
| :param size: workload size | ||
| :param input_buckets: input storage containers for this benchmark | ||
| :param output_buckets: | ||
| :param upload_func: upload function taking three params(bucket_idx, key, filepath) | ||
| :param size: workload size (e.g., "test", "small", "large") | ||
| :param benchmarks_bucket: name of the benchmark bucket | ||
| :param input_paths: list of input prefixes (one per input bucket) | ||
| :param output_paths: list of output prefixes (one per output bucket) | ||
| :param upload_func: upload function taking three params (bucket_idx, key, filepath) | ||
| :param nosql_func: not used here | ||
| ''' | ||
| def generate_input(data_dir, size, benchmarks_bucket, input_paths, output_paths, upload_func, nosql_func): | ||
| last_key = None | ||
|
|
||
| # Upload all .mp4 files from data_dir to bucket 0 | ||
| for file in glob.glob(os.path.join(data_dir, '*.mp4')): | ||
| img = os.path.relpath(file, data_dir) | ||
| upload_func(0, img, file) | ||
| #TODO: multiple datasets | ||
| key = os.path.relpath(file, data_dir) | ||
| upload_func(0, key, file) | ||
| last_key = key | ||
|
|
||
| if last_key is None: | ||
| raise RuntimeError(f"No .mp4 files found in {data_dir}") | ||
|
|
||
| input_config = {'object': {}, 'bucket': {}} | ||
| input_config['object']['key'] = img | ||
| input_config['object']['op'] = 'watermark' | ||
| input_config['object']['duration'] = 1 | ||
|
|
||
| # Use the last uploaded file as the input object | ||
| input_config['object']['key'] = last_key | ||
| input_config['object']['op'] = 'gpu-filter' # must match your handler's operations dict | ||
| input_config['object']['duration'] = 1 # seconds of video to process | ||
| input_config['object']['num_iters'] = 10 # extra param for GPU workload intensity | ||
|
|
||
| input_config['bucket']['bucket'] = benchmarks_bucket | ||
| input_config['bucket']['input'] = input_paths[0] | ||
| input_config['bucket']['output'] = output_paths[0] | ||
|
|
||
| return input_config |
Uh oh!
There was an error while loading. Please reload this page.