diff --git a/Selenium/antmedia-samples.py b/Selenium/antmedia-samples.py new file mode 100644 index 00000000..1a315b1e --- /dev/null +++ b/Selenium/antmedia-samples.py @@ -0,0 +1,324 @@ +import os +import time +import json +import shlex +import requests +import subprocess +import urllib.parse +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.chrome.options import Options + + +# Function to send notification to Slack +def send_slack_message(webhook_url, message, icon_emoji=":x:"): + payload = { + "text": message, + "icon_emoji": icon_emoji + } + response = requests.post(webhook_url, data={"payload": json.dumps(payload)}) + + if response.status_code != 200: + print("Error sending Slack message: ", response.text) + else: + print("Slack message sent successfully!") + + +# Function to start FFMPEG process +def publish_with_ffmpeg(url, protocol='rtmp'): + if protocol == 'rtmp': + # Start FFmpeg process for RTMP streaming + quoted_url = shlex.quote(url) + ffmpeg_command = 'ffmpeg -re -f lavfi -i smptebars -c:v libx264 -preset veryfast -tune zerolatency -profile:v baseline -c:a aac -b:a 128k -t 30 -f flv' + ' ' + quoted_url + ffmpeg_args = shlex.split(ffmpeg_command) + ffmpeg_process = subprocess.Popen(ffmpeg_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = ffmpeg_process.communicate() + + elif protocol == 'srt': + # Start FFmpeg process for SRT streaming + quoted_url = urllib.parse.quote(url, safe=':/?=') + ffmpeg_command = 'ffmpeg -f lavfi -re -i smptebars=duration=60:size=1280x720:rate=30 -f lavfi -re -i sine=frequency=1000:duration=60:sample_rate=44100 -pix_fmt yuv420p -c:v libx264 -b:v 1000k -g 30 -keyint_min 120 -profile:v baseline -preset veryfast -t 30 -f mpegts udp://127.0.0.1:5000?pkt_size=1316' + ffmpeg_args = shlex.split(ffmpeg_command) + ffmpeg_process = subprocess.Popen(ffmpeg_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + srt_command = ['srt-live-transmit', 'udp://127.0.0.1:5000', '-t', '30', quoted_url] + srt_process = subprocess.Popen(srt_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = ffmpeg_process.communicate() + srt_stdout, srt_stderr = srt_process.communicate() + srt_exit_code = srt_process.returncode + return srt_exit_code + + +# Function to close the previous tabs before starting the new test +def switch_to_first_tab(driver): + if len(driver.window_handles) > 1: + driver.close() + driver.switch_to.window(driver.window_handles[0]) + + +# Function to remove advertisement from sample pages +def remove_ad(driver): + element = driver.find_element(By.XPATH, "/html/body/div[3]/div") + driver.execute_script("arguments[0].style.display = 'none';", element) + + +# Function to switch to new window and close the advertisement block +def switch_window_and_frame(driver): + driver.switch_to.window(driver.window_handles[1]) + time.sleep(10) + remove_ad(driver) + time.sleep(2) + driver.switch_to.frame(0) + time.sleep(3) + + +webhook_url = os.environ['WEBHOOK_URL'] +icon_emoji = ":x:" + +options = Options() +options.add_argument('--headless') +options.add_argument("--use-fake-ui-for-media-stream") +options.add_argument("--use-fake-device-for-media-stream") +driver = webdriver.Chrome(options=options) +driver.maximize_window() + +driver.get("https://antmedia.io/webrtc-samples/") + +# Testing Virtual Background Sample Page +for i in range(2): + try: + driver.execute_script("window.open('https://antmedia.io/webrtc-samples/webrtc-virtual-background/', '_blank');") + driver.switch_to.window(driver.window_handles[1]) + time.sleep(20) + remove_ad(driver) + time.sleep(2) + driver.switch_to.frame(0) + time.sleep(3) + driver.find_element(By.XPATH, "/html/body/div/div/div[4]/div[3]/img").click() + time.sleep(5) + driver.find_element(By.XPATH, "/html/body/div/div/div[7]/button[1]").click() + time.sleep(15) + driver.find_element(By.XPATH, "/html/body/div/div/div[7]/button[2]").click() + time.sleep(3) + print("WebRTC virtual background is successful") + break + + except: + if i==1: + message = "Virtual background test is failed, check -> https://antmedia.io/webrtc-samples/webrtc-virtual-background/" + send_slack_message(webhook_url, message, icon_emoji) + continue + +switch_to_first_tab(driver) + +# Testing WebRTC and HLS Comparison Live Demo Page +try: + driver.execute_script("window.open('https://antmedia.io/live-demo/', '_blank');") + driver.switch_to.window(driver.window_handles[1]) + time.sleep(20) + remove_ad(driver) + time.sleep(2) + driver.find_element(By.XPATH, "/html/body/div[5]/div/article[2]/div[2]/div[1]/div[1]/div/div/p/button[1]").click() + time.sleep(15) + driver.find_element(By.XPATH, "/html/body/div[5]/div/article[2]/div[2]/div[1]/div[1]/div/div/p/button[2]").click() + time.sleep(3) + print("Live demo is successful") + +except: + message = "Livedemo test is failed, check -> https://antmedia.io/live-demo/" + send_slack_message(webhook_url, message, icon_emoji) + +switch_to_first_tab(driver) + +# Testing WebRTC to WebRTC Sample Page +try: + driver.execute_script("window.open('https://antmedia.io/webrtc-samples/webrtc-publish-webrtc-play/', '_blank');") + switch_window_and_frame(driver) + driver.find_element(By.XPATH, "/html/body/div/div/div[8]/button[1]").click() + time.sleep(10) + driver.find_element(By.XPATH, "/html/body/div/div/div[7]/div[1]/a").click() + time.sleep(5) + driver.find_element(By.XPATH, "/html/body/div/div/div[8]/button[2]").click() + time.sleep(3) + print("WebRTC to WebRTC is successful") + +except: + message = "WebRTC to WebRTC test is failed, check -> https://antmedia.io/webrtc-samples/webrtc-publish-webrtc-play/" + send_slack_message(webhook_url, message, icon_emoji) + +driver.close() +driver.switch_to.window(driver.window_handles[1]) +switch_to_first_tab(driver) + +# Testing WebRTC to HLS Sample Page +try: + driver.execute_script("window.open('https://antmedia.io/webrtc-samples/webrtc-publish-hls-play/', '_blank');") + switch_window_and_frame(driver) + driver.find_element(By.XPATH, "/html/body/div/div/div[8]/button[1]").click() + time.sleep(10) + driver.find_element(By.XPATH, "/html/body/div/div/div[7]/div[1]/a").click() + time.sleep(5) + driver.find_element(By.XPATH, "/html/body/div/div/div[8]/button[2]").click() + time.sleep(5) + print("WebRTC to HLS is successful") + +except: + message = "WebRTC to HLS test is failed, check -> https://antmedia.io/webrtc-samples/webrtc-publish-hls-play/" + send_slack_message(webhook_url, message, icon_emoji) + +driver.close() +driver.switch_to.window(driver.window_handles[1]) +switch_to_first_tab(driver) + +# Testing WebRTC audio publish sample page +try: + driver.execute_script("window.open('https://antmedia.io/webrtc-samples/webrtc-audio-publish-play/', '_blank');") + switch_window_and_frame(driver) + driver.find_element(By.XPATH, "/html/body/div/div/div[6]/button[1]").click() + time.sleep(3) + driver.find_element(By.XPATH, "/html/body/div/div/div[5]/div[1]/a").click() + driver.switch_to.window(driver.window_handles[2]) + time.sleep(2) + driver.switch_to.frame(0) + time.sleep(2) + driver.find_element(By.XPATH, "/html/body/div/div/div[4]/button[1]").click() + time.sleep(10) + driver.find_element(By.XPATH, "/html/body/div/div/div[4]/button[2]").click() + time.sleep(2) + print("WebRTC audio publish and play is successful") + +except: + message = "WebRTC audio publish test is failed, check -> https://antmedia.io/webrtc-samples/webrtc-audio-publish-play/" + send_slack_message(webhook_url, message, icon_emoji) + +driver.close() +driver.switch_to.window(driver.window_handles[1]) +switch_to_first_tab(driver) + +# Testing RTMP to WebRTC sample page +try: + driver.execute_script("window.open('https://antmedia.io/webrtc-samples/rtmp-publish-webrtc-play/', '_blank');") + switch_window_and_frame(driver) + rtmp_element = driver.find_element(By.XPATH, "/html/body/div/div/div[3]/div[1]/div") + url = rtmp_element.text + publish_with_ffmpeg(url, protocol='rtmp') + print("RTMP to WebRTC is successful") + +except: + message = "RTMP to WebRTC test is failed, check -> https://antmedia.io/webrtc-samples/rtmp-publish-wertc-play/" + send_slack_message(webhook_url, message, icon_emoji) + +switch_to_first_tab(driver) + +# Testing RTMP to HLS sample page +try: + driver.execute_script("window.open('https://antmedia.io/webrtc-samples/rtmp-publish-hls-play/', '_blank');") + switch_window_and_frame(driver) + rtmp_element = driver.find_element(By.XPATH, "/html/body/div/div/div[3]/div[1]/div") + url = rtmp_element.text + publish_with_ffmpeg(url, protocol='rtmp') + print("RTMP to HLS is successful") + +except: + message = "RTMP to HLS test is failed, check -> https://antmedia.io/webrtc-samples/rtmp-publish-hls-play/" + send_slack_message(webhook_url, message, icon_emoji) + +switch_to_first_tab(driver) + +# Testing SRT to WebRTC sample page +try: + driver.execute_script("window.open('https://antmedia.io/webrtc-samples/srt-publish-webrtc-play/', '_blank');") + switch_window_and_frame(driver) + srt_element = driver.find_element(By.XPATH, "/html/body/div/div/div[3]/div[1]/div") + url = srt_element.text + srt_exit_code = publish_with_ffmpeg(url, protocol='srt') + if srt_exit_code == 0: + print("SRT to WebRTC is successful") + else: + raise Exception("SRT to WebRTC test is failed") + +except: + message = "SRT to WebRTC test is failed, check -> https://antmedia.io/webrtc-samples/srt-publish-webrtc-play/" + send_slack_message(webhook_url, message, icon_emoji) + +switch_to_first_tab(driver) + +# Testing SRT to HLS sample page +try: + driver.execute_script("window.open('https://antmedia.io/webrtc-samples/srt-publish-hls-play/', '_blank');") + switch_window_and_frame(driver) + srt_element = driver.find_element(By.XPATH, "/html/body/div/div/div[3]/div[1]/div") + url = srt_element.text + srt_exit_code = publish_with_ffmpeg(url, protocol='srt') + if srt_exit_code == 0: + print("SRT to HLS is successful") + else: + raise Exception("SRT to HLS test is failed") + +except: + message = "SRT to HLS test is failed, check -> https://antmedia.io/webrtc-samples/srt-publish-hls-play/" + send_slack_message(webhook_url, message, icon_emoji) + +switch_to_first_tab(driver) + +# Testing DeepAR sample page +try: + driver.execute_script("window.open('https://antmedia.io/webrtc-samples/deepar-publish-play/', '_blank');") + switch_window_and_frame(driver) + driver.find_element(By.XPATH, "/html/body/div/div/select").click() + time.sleep(1) + driver.find_element(By.XPATH, "/html/body/div[1]/div/select/option[6]").click() + time.sleep(1) + driver.find_element(By.XPATH, "/html/body/div[1]/div/div[5]/button[1]").click() + time.sleep(10) + driver.find_element(By.XPATH, "/html/body/div[1]/div/div[5]/button[2]").click() + print("WebRTC Deep AR test is successful") + +except: + message = "WebRTC DeepAR sample test is failed, check -> https://antmedia.io/webrtc-samples/deepar-publish-play/" + send_slack_message(webhook_url, message, icon_emoji) + +switch_to_first_tab(driver) + +# Testing Whiteboard sample page +try: + driver.execute_script("window.open('https://antmedia.io/webrtc-samples/interactive-whiteboard-publish-play/', '_blank');") + switch_window_and_frame(driver) + driver.switch_to.frame("webrtc-publish-frame") + time.sleep(1) + driver.find_element(By.XPATH, "/html/body/div[2]/div/div/div/div[2]/div[6]/button[1]").click() + time.sleep(5) + driver.switch_to.frame(0) + driver.find_element(By.XPATH, "/html/body/section[2]/canvas[4]").click() + time.sleep(2) + driver.switch_to.default_content() + driver.switch_to.frame(0) + driver.switch_to.frame("webrtc-play-frame") + driver.find_element(By.XPATH, "/html/body/div[2]/div/div/div/div[3]/div[4]/button[1]").click() + time.sleep(10) + print("WebRTC white board test is successful") + +except: + message = "WebRTC whiteboard test is failed, check -> https://antmedia.io/webrtc-samples/interactive-whiteboard-publish-play/" + send_slack_message(webhook_url, message, icon_emoji) + +switch_to_first_tab(driver) + +# Testing WebRTC data channel sample page +try: + driver.execute_script("window.open('https://antmedia.io/webrtc-samples/webrtc-data-channel-only/', '_blank');") + switch_window_and_frame(driver) + driver.find_element(By.XPATH, "/html/body/div/div/div[6]/button[1]").click() + time.sleep(5) + text = driver.find_element(By.ID, 'dataTextbox') + text.send_keys("Hello, how are you ?") + driver.find_element(By.XPATH, "/html/body/div/div/div[3]/div/div[2]/button").click() + time.sleep(20) + driver.find_element(By.XPATH, "/html/body/div/div/div[6]/button[2]").click() + time.sleep(3) + print("WebRTC data channel is successful") + +except: + message = "WebRTC data channel test is failed, check -> https://antmedia.io/webrtc-samples/webrtc-data-channel-only/" + send_slack_message(webhook_url, message, icon_emoji) + +driver.quit() diff --git a/aws/ant_media_server_aws_cluster_update.py b/aws/ant_media_server_aws_cluster_update.py new file mode 100644 index 00000000..daf977dc --- /dev/null +++ b/aws/ant_media_server_aws_cluster_update.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python + +import boto3, time, argparse + +""" +ant_media_server_aws_cluster_update.py + +Description: This script terminates the existing instances (except MongoDB) and recreates them with a new image. + +Usage: + python ant_media_server_aws_cluster_update.py --origin --edge + +Options: + -h, --help Show this help message and exit + --origin Specify the name of the origin autoscaling group + --edge Specify the name of the edge autoscaling group + +Examples: + python ant_media_server_aws_cluster_update.py --origin origin-auto-scaling-group --edge edge-auto-scaling-group +""" + +if __name__ == "__main__": + # Parse command line arguments + parser = argparse.ArgumentParser(description='Your script description') + parser.add_argument('--origin', type=str, help='Specify the name of the origin autoscaling group') + parser.add_argument('--edge', type=str, help='Specify the name of the edge autoscaling group') + args = parser.parse_args() + + # Assign the provided autoscaling group names to the respective variables + origin_group_name = args.origin + edge_group_name = args.edge + + +ec2_client = boto3.client('ec2') +autoscaling_client = boto3.client('autoscaling') + +# Ant Media Server Enterprise Edition details +Name="AntMedia-AWS-Marketplace-EE-*" +Arch="x86_64" +Owner="679593333241" + +# Get the latest AMI of Ant Media Server +def image_id(): + + image_response = boto3.client('ec2').describe_images( + Owners=[Owner], + Filters=[ + {'Name': 'name', 'Values': [Name]}, + {'Name': 'architecture', 'Values': [Arch]}, + {'Name': 'root-device-type', 'Values': ['ebs']}, + ], + ) + + ami = sorted(image_response['Images'], + key=lambda x: x['CreationDate'], + reverse=True) + return (ami[0]['ImageId']) + +class AutoscaleSettings: + def __init__(self, autoscaling_client, group_name): + self.autoscaling_client = autoscaling_client + self.group_name = group_name + self.desired_capacity = None + self.min_size = None + self.max_size = None + + def retrieve_settings(self): + response = self.autoscaling_client.describe_auto_scaling_groups(AutoScalingGroupNames=[self.group_name]) + auto_scaling_group = response['AutoScalingGroups'][0] + + self.desired_capacity = auto_scaling_group['DesiredCapacity'] + self.min_size = auto_scaling_group['MinSize'] + self.max_size = auto_scaling_group['MaxSize'] + self.launch_config_name = auto_scaling_group['LaunchConfigurationName'] + response_1 = autoscaling_client.describe_launch_configurations( + LaunchConfigurationNames=[self.launch_config_name] + ) + self.launch_configuration = response_1['LaunchConfigurations'][0] + def update_autoscale(self): + print ("#########", self.group_name, "#########") + print("The instances are terminating.") + terminate_instances = autoscaling_client.update_auto_scaling_group( + AutoScalingGroupName=self.group_name, + MinSize=0, + DesiredCapacity=0, + MaxSize=0, + ) + time.sleep(10) + print ("Creating new Launch Templates") + create_new_launch_template = autoscaling_client.create_launch_configuration( + LaunchConfigurationName=self.launch_config_name + '-updated', + ImageId=str(image_id()), + InstanceType=self.launch_configuration['InstanceType'], + ) + time.sleep(5) + print ("Updating the Auto-Scaling Groups") + update_autoscale = autoscaling_client.update_auto_scaling_group( + AutoScalingGroupName=self.group_name, + LaunchConfigurationName=self.launch_config_name + '-updated' + + ) + time.sleep(10) + print ("New instances are creating with the latest version of Ant Media Server EE") + update_capacity = autoscaling_client.update_auto_scaling_group( + AutoScalingGroupName=self.group_name, + MinSize=self.min_size, + DesiredCapacity=self.desired_capacity, + MaxSize=self.max_size, + ) + print("##################") + + +# Create an instance of the AutoscaleSettings class +origin_settings = AutoscaleSettings(autoscaling_client, origin_group_name) +edge_settings = AutoscaleSettings(autoscaling_client, edge_group_name) + +# Get settings +origin_settings.retrieve_settings() +edge_settings.retrieve_settings() + +# Update Auto-Scaling +origin_settings.update_autoscale() +edge_settings.update_autoscale() diff --git a/docker/Dockerfile_Process b/docker/Dockerfile_Process index f25b2dec..dd1534af 100644 --- a/docker/Dockerfile_Process +++ b/docker/Dockerfile_Process @@ -5,6 +5,7 @@ FROM ubuntu:20.04 ARG AntMediaServer +ARG LicenseKey ARG BranchName=master @@ -18,10 +19,16 @@ RUN cd home \ && wget https://raw.githubusercontent.com/ant-media/Scripts/${BranchName}/install_ant-media-server.sh \ && chmod 755 install_ant-media-server.sh -RUN cd home \ +RUN cd /home \ && pwd \ - && ./install_ant-media-server.sh -i ${AntMediaServer} -s false - + && if [ -n "$AntMediaServer" ]; then \ + ./install_ant-media-server.sh -i ${AntMediaServer} -s false; \ + elif [ -n "$LicenseKey" ]; then \ + ./install_ant-media-server.sh -l ${LicenseKey} -s false; \ + else \ + echo "Both AntMediaServer and LicenseKey arguments are not provided. Aborting the build process."; \ + exit 1; \ + fi # Options # -g: Use global(Public) IP in network communication. Its value can be true or false. Default value is false. diff --git a/install_ant-media-server.sh b/install_ant-media-server.sh index 3b443342..fd668965 100755 --- a/install_ant-media-server.sh +++ b/install_ant-media-server.sh @@ -287,8 +287,9 @@ REQUIRED_VERSION="2.6" if [ "$ID" == "ubuntu" ]; then $SUDO apt-get update -y $SUDO apt-get install unzip zip libva-drm2 libva-x11-2 libvdpau-dev -y - VERSION=$(unzip -p "$ANT_MEDIA_SERVER_ZIP_FILE" ant-media-server/ant-media-server.jar | busybox unzip -p - | grep -a "Implementation-Version"|cut -d' ' -f2 | tr -d '\r') - + $SUDO unzip -o $ANT_MEDIA_SERVER_ZIP_FILE "ant-media-server/ant-media-server.jar" -d /tmp/ + VERSION=$(unzip -p /tmp/ant-media-server/ant-media-server.jar | grep -a "Implementation-Version"|cut -d' ' -f2 | tr -d '\r') + # If the version is lower than 2.6 and the architecture is x86_64, install the libcrystalhd-dev package. # Additionally, arm64 architecture does not have libcrystalhd-dev and the following check will fix the installation problem in ARM. # After 2.6, there is no dependency to libcrystalhd-dev @@ -357,7 +358,7 @@ else $SUDO apt-get install openjdk-11-jre-headless -y check elif [ "$ID" == "centos" ] || [ "$ID" == "almalinux" ] || [ "$ID" == "rocky" ] || [ "$ID" == "rhel" ]; then - $SUDO yum -y install java-11-openjdk-headless + $SUDO yum -y install java-11-openjdk-headless tzdata-java ln -s $(readlink -f $(which java) | rev | cut -d "/" -f3- | rev) /usr/lib/jvm/java-11-openjdk-amd64 fi echo "export JAVA_HOME=\/usr\/lib\/jvm\/java-11-openjdk-amd64/" >>~/.bashrc @@ -418,7 +419,7 @@ then fi # create a logrotate config file -cat << EOF | $SUDO tee /etc/logrotate.d/antmedia +cat << EOF | $SUDO tee /etc/logrotate.d/antmedia > /dev/null /var/log/antmedia/antmedia-error.log { daily create 644 antmedia antmedia @@ -433,6 +434,20 @@ cat << EOF | $SUDO tee /etc/logrotate.d/antmedia reload rsyslog >/dev/null 2>&1 || true endscript } +/var/log/antmedia/0.0.0.0_access*.log { + daily + create 644 antmedia antmedia + rotate 7 + maxsize 50M + compress + delaycompress + copytruncate + notifempty + sharedscripts + postrotate + reload rsyslog >/dev/null 2>&1 || true + endscript +} EOF check diff --git a/install_mongodb.sh b/install_mongodb.sh index 50864959..916a2571 100644 --- a/install_mongodb.sh +++ b/install_mongodb.sh @@ -2,6 +2,20 @@ # # MongoDB Installation Script # + +help() { + echo "Usage: $0" + echo "" + echo "Options:" + echo " --auto-create Automatically create a MongoDB user with a random username and password" + echo " --help Show this help menu" +} + +if [ "$1" == "--help" ]; then + help + exit 1 +fi + export DEBIAN_FRONTEND=noninteractive sudo apt-get update sudo apt-get install -y curl gnupg2 @@ -9,3 +23,32 @@ curl -fsSL https://pgp.mongodb.com/server-6.0.asc | sudo gpg --dearmor --yes --o echo "deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-6.0.gpg] https://repo.mongodb.org/apt/ubuntu $(lsb_release -cs)/mongodb-org/6.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list sudo apt-get update sudo apt-get install -y mongodb-org + +if [[ $1 == "--auto-create" ]]; then + # Generate a random username and password + username=$(openssl rand -hex 6) + password=$(openssl rand -hex 12) + + # Start MongoDB and configure authentication + sudo systemctl restart mongod + sudo systemctl enable mongod + + sleep 10 + + echo "use admin; + db.createUser({ user: '$username', pwd: '$password', roles: ['root'], mechanisms: ['SCRAM-SHA-1'] });" | mongosh + + sudo sed -i 's/#security:/security:\n authorization: "enabled"/g' /etc/mongod.conf + sudo sed -i 's/bindIp:.*/bindIp: 0.0.0.0/g' /etc/mongod.conf + sudo systemctl restart mongod + + echo "MongoDB username: $username" + echo "MongoDB password: $password" +else + # Start MongoDB without authentication + sudo sed -i 's/bindIp:.*/bindIp: 0.0.0.0/g' /etc/mongod.conf + sudo systemctl restart mongod + sudo systemctl enable mongod + + echo "MongoDB installed without username and password" +fi diff --git a/vod_transcode.sh b/vod_transcode.sh index 56c3ce93..942377f2 100644 --- a/vod_transcode.sh +++ b/vod_transcode.sh @@ -26,4 +26,4 @@ c=("320x240" "800k") cd /usr/local/antmedia/webapps/$AMS_APP_NAME/streams/ -$(command -v ffmpeg) -i $file -map 0:v:0 -map 0:a:0 -map 0:v:0 -map 0:a:0 -map 0:v:0 -map 0:a:0 -s:v:0 ${a[0]} -c:v:0 libx264 -b:v:0 ${a[1]} -s:v:1 ${b[0]} -c:v:1 libx264 -b:v:1 ${b[1]} -s:v:2 ${c[0]} -c:v:2 libx264 -b:v:2 ${c[1]} -c:a aac -f hls -hls_playlist_type vod -master_pl_name ${file_name}.m3u8 -hls_segment_filename ${file_name}_%v/${file_name}%03d.ts -use_localtime_mkdir 1 -var_stream_map "v:0,a:0,name:720p v:1,a:1,name:480p v:2,a:2,name:360p" ${file_name}_%v.m3u \ No newline at end of file +$(command -v ffmpeg) -i $file -map 0:v:0 -map 0:a:0 -map 0:v:0 -map 0:a:0 -map 0:v:0 -map 0:a:0 -s:v:0 ${a[0]} -c:v:0 libx264 -b:v:0 ${a[1]} -s:v:1 ${b[0]} -c:v:1 libx264 -b:v:1 ${b[1]} -s:v:2 ${c[0]} -c:v:2 libx264 -b:v:2 ${c[1]} -c:a aac -f hls -hls_playlist_type vod -master_pl_name ${file_name}.m3u8 -hls_segment_filename ${file_name}_%v/${file_name}%04d.ts -use_localtime_mkdir 1 -var_stream_map "v:0,a:0,name:720p v:1,a:1,name:480p v:2,a:2,name:360p" ${file_name}_%v.m3u8