Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Bash scripts to create VOD HLS stream with ffmpeg (Extended version)
#!/usr/bin/env bash
START_TIME=$SECONDS
set -e
echo "-----START GENERATING HLS STREAM-----"
# Usage create-vod-hls.sh SOURCE_FILE [OUTPUT_NAME]
[[ ! "${1}" ]] && echo "Usage: create-vod-hls.sh SOURCE_FILE [OUTPUT_NAME]" && exit 1
# comment/add lines here to control which renditions would be created
renditions=(
# resolution bitrate audio-rate
"426x240 400k 128k"
"640x360 800k 128k"
"842x480 1400k 192k"
"1280x720 2800k 192k"
"1920x1080 5000k 256k"
)
segment_target_duration=10 # try to create a new segment every 10 seconds
max_bitrate_ratio=1.07 # maximum accepted bitrate fluctuations
rate_monitor_buffer_ratio=1.5 # maximum buffer size between bitrate conformance checks
#########################################################################
source="${1}"
target="${2}"
if [[ ! "${target}" ]]; then
target="${source##*/}" # leave only last component of path
target="${target%.*}" # strip extension
fi
mkdir -p ${target}
# ----CUSTOM----
sourceResolution="$(ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=s=x:p=0 ${source})"
# echo ${sourceResolution}
arrIN=(${sourceResolution//x/ })
sourceWidth="${arrIN[0]}"
sourceHeight="${arrIN[1]}"
echo ${sourceWidth}
echo ${sourceHeight}
sourceAudioBitRate="$(ffprobe -v error -select_streams a:0 -show_entries stream=bit_rate -of csv=s=x:p=0 ${source})"
sourceAudioBitRateFormatted=$((sourceAudioBitRate / 1000))
# ----END CUSTOM----
key_frames_interval="$(echo `ffprobe ${source} 2>&1 | grep -oE '[[:digit:]]+(.[[:digit:]]+)? fps' | grep -oE '[[:digit:]]+(.[[:digit:]]+)?'`*2 | bc || echo '')"
key_frames_interval=${key_frames_interval:-50}
key_frames_interval=$(echo `printf "%.1f\n" $(bc -l <<<"$key_frames_interval/10")`*10 | bc) # round
key_frames_interval=${key_frames_interval%.*} # truncate to integer
# static parameters that are similar for all renditions
static_params="-c:a aac -ar 48000 -c:v h264 -profile:v main -crf 19 -sc_threshold 0"
static_params+=" -g ${key_frames_interval} -keyint_min ${key_frames_interval} -hls_time ${segment_target_duration}"
static_params+=" -hls_playlist_type vod"
# misc params
misc_params="-hide_banner -y"
master_playlist="#EXTM3U
#EXT-X-VERSION:3
"
cmd=""
resolutionValid=0
prevHeight=0
for rendition in "${renditions[@]}"; do
# drop extraneous spaces
rendition="${rendition/[[:space:]]+/ }"
# rendition fields
resolution="$(echo ${rendition} | cut -d ' ' -f 1)"
bitrate="$(echo ${rendition} | cut -d ' ' -f 2)"
audiorate="$(echo ${rendition} | cut -d ' ' -f 3)"
audioBitRateFormatted=${audiorate%?} # remove "k" at the last index
# take highest possible audio bit rate
if [ $audioBitRateFormatted -gt $sourceAudioBitRateFormatted ]; then
audiorate=${sourceAudioBitRateFormatted}k
fi
# calculated fields
width="$(echo ${resolution} | grep -oE '^[[:digit:]]+')"
height="$(echo ${resolution} | grep -oE '[[:digit:]]+$')"
maxrate="$(echo "`echo ${bitrate} | grep -oE '[[:digit:]]+'`*${max_bitrate_ratio}" | bc)"
bufsize="$(echo "`echo ${bitrate} | grep -oE '[[:digit:]]+'`*${rate_monitor_buffer_ratio}" | bc)"
bandwidth="$(echo ${bitrate} | grep -oE '[[:digit:]]+')000"
name="${height}p"
if [ $sourceHeight -le $prevHeight ]; then
echo "video source has height smaller than output height (${height})"
break
fi
widthParam=0
heightParam=0
if [ $(((width / sourceWidth) * sourceHeight)) -gt $height ]; then
widthParam=-2
heightParam=$height
else
widthParam=$width
heightParam=-2
fi
cmd+=" ${static_params} -vf scale=w=${widthParam}:h=${heightParam}"
cmd+=" -b:v ${bitrate} -maxrate ${maxrate%.*}k -bufsize ${bufsize%.*}k -b:a ${audiorate}"
cmd+=" -hls_segment_filename ${target}/${name}_%03d.ts ${target}/${name}.m3u8"
# add rendition entry in the master playlist
master_playlist+="#EXT-X-STREAM-INF:BANDWIDTH=${bandwidth},RESOLUTION=${resolution}\n${name}.m3u8\n"
resolutionValid=1
prevHeight=${height}
done
if [ $resolutionValid -eq 1 ]; then
# start conversion
echo -e "Executing command:\nffmpeg ${misc_params} -i ${source} ${cmd}\n"
ffmpeg ${misc_params} -i ${source} ${cmd}
# create master playlist file
echo -e "${master_playlist}" > ${target}/playlist.m3u8
echo "Done - encoded HLS is at ${target}/"
else
echo "Video source is too small"
exit 1
fi
ELAPSED_TIME=$(($SECONDS - $START_TIME))
echo "Elapsed time: ${ELAPSED_TIME}"
echo "-----FINISH GENERATING HLS STREAM-----"
@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Jun 5, 2020

This is extended version of the original one.

What's in this version?

This version contains following improvements:

  • Auto generate bitrate ladder based on the video source. Eg: if the video source is within 1080p and 720p, output bitrates will start from 1080p, 720p, then 480, and so on
  • Correctly scale video source: the original version has problem in case the video after scaling has width or height is not divisible by 2. This version fix that by remove force_original_aspect_ratio, and using -2 value when scaling to make the dimensions divisible by 2 . This should be aware because I had a video with resolution 320x176 and after scaling it to match 480p resolution, its height is not divisible by 2 and it caused runtime error
  • Keep screen ratio of the original video by determine which dimension should be scaled from (width or height) by checking:
if [ $(((width / sourceWidth) * sourceHeight)) -gt $height ]

How to run

# install a small library for math expression
sudo apt-get install -y bc

bash create-vod-hls.sh video.mp4
@yondoofree

This comment has been minimized.

Copy link

@yondoofree yondoofree commented Jun 25, 2020

Thank you for the improvements. Now if I could figure out how to add an image overlay to each profile that displayed the video size, ie. an image "1080p.png" for the 1080 profile, "720p.png" for the 720 profile, etc.

@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Jun 25, 2020

In that case:

  1. you should use -filter_complex instead of -vf like current implementation.
  2. In the current approach, we chain multiple resolutions command using for loop and after the loop is finished, we actually run only 1 single ffmpeg command. But for your case we won't chain anymore, we'll run ffmpeg directly inside the for loop for each resolution. The reason for this is to able to use different overlay images as filter as I mentioned in previous step
@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Jun 28, 2020

Update

  • Improve auto generating bitrate ladder
  • Added time elapse (for debugging purpose only)
  • Changed CRF from 20 -> 19 as in my case I want my video has a bit more quality. You can change this to your need, smaller value gives better quality. But remember this should always be greater than or equal 18 or your file size will significant larger
  • Updated renditions to have audio bitrate a bit higher. Reason: I notice that the original setup from the original script is not good in my case, some of my videos have really bad quality. Detail: in line 78, compare audio bitrate in renditions at current loop with audio source bitrate to make sure our choice never exceeds the bitrate from audio source

if audio bitrate in renditions is not good for you, then change it to match your needs.

@xubmuajkub

This comment has been minimized.

Copy link

@xubmuajkub xubmuajkub commented Jul 15, 2020

My video always has only 1080p output, no matter I try using 1080p or 720p input. Maybe you can check again.

I have some improvement.

Add this line so that we have just a single file, instead of many .ts file
cmd+=" -hls_flags single_file"
If we add the above line this line, we should modified the set filename line like this so that the name is consistent
cmd+=" -hls_segment_filename ${target}/${name}.ts ${target}/${name}.m3u8"

@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Jul 15, 2020

My video always has only 1080p output, no matter I try using 1080p or 720p input. Maybe you can check again.

I have some improvement.

# Add this line so that we have just a single file, instead of many .ts file
cmd+=" -hls_flags single_file"
# If we add the above line this line should modified like this so that the name is consistent
cmd+=" -hls_segment_filename ${target}/${name}.ts ${target}/${name}.m3u8"

Just tested my script, it works fine.

Test video resolution: 1280x720 (720p). Output: 240p, 360p, 480p, 720p

Can you share your video so I can have a test with it?

About your improvement: I think the concept of HLS is reduce the load at a time for user by splitting video into multiple chunks for each resolution (Apple recommends 10 seconds for each chunk). So if you just output only 1 .ts file for each resolution, that's not what we usually want when talking about HLS :)

@xubmuajkub

This comment has been minimized.

Copy link

@xubmuajkub xubmuajkub commented Jul 15, 2020

My video always has only 1080p output, no matter I try using 1080p or 720p input. Maybe you can check again.
I have some improvement.

# Add this line so that we have just a single file, instead of many .ts file
cmd+=" -hls_flags single_file"
# If we add the above line this line should modified like this so that the name is consistent
cmd+=" -hls_segment_filename ${target}/${name}.ts ${target}/${name}.m3u8"

Just tested my script, it works fine.

Test video resolution: 1280x720 (720p). Output: 240p, 360p, 480p, 720p

Can you share your video so I can have a test with it?

About your improvement: I think the concept of HLS is reduce the load at a time for user by splitting video into multiple chunks for each resolution (Apple recommends 10 seconds for each chunk). So if you just output only 1 .ts file for each resolution, that's not what we usually want when talking about HLS :)

I see my issue. Because I want to have the highest resolution at the top, so I switch the renditions like this

  "1920x1080  5000k    192k"
  "1280x720   2800k    128k"
  "842x480    1400k    128k"
  "640x360    800k     96k"

That's why it's not working. I tried switch back and it's working OK now.

About my improvement, you can try to see the result. As I tested, I still got the partial content from response when I stream the video. I think there shouldn't be any problem with that.

You can test it here for single .ts file. I've tried using it on my friend's site.

@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Jul 15, 2020

@xubmuajkub Sorry, I misunderstood your idea.

I'm not sure if all players out there support BYTE-RANGE at the moment.

Thanks for your comment, I think I'll add an option to my script so user can switch between single and multiple output .ts mode

@xubmuajkub

This comment has been minimized.

Copy link

@xubmuajkub xubmuajkub commented Jul 15, 2020

@xubmuajkub Sorry, I misunderstood your idea.

I'm not sure if all players out there support BYTE-RANGE at the moment.

Thanks for your comment, I think I'll add an option to my script so user can switch between single and multiple output .ts mode

Thanks. That would be good since we have more options. For me, I don't want to mess with hundreds of chunked files. So I choose this way.

Sorry if I missed understand you because I'm totally new to video streaming service :)

@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Jul 20, 2020

Update

Add script for running FFMPEG with GPU to reduce significant time to generate HLS stream. Check here

@0crypto0

This comment has been minimized.

Copy link

@0crypto0 0crypto0 commented Aug 5, 2020

Hi can you add an example using fmp4?

@kashkashio

This comment has been minimized.

Copy link

@kashkashio kashkashio commented Aug 19, 2020

You are a life saver ! Works great !
Tho i wonder for example how dose safari determines which resultion to take from the playlist?
By screen size? by network speed? latency etc... ?

P.S
How can i force it to split to even more chunks ? it dose like 3 chunks for 22 seconds video
Can i force it into more ?

@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Aug 19, 2020

You are a life saver ! Works great !
Tho i wonder for example how dose safari determines which resultion to take from the playlist?
By screen size? by network speed? latency etc... ?

P.S
How can i force it to split to even more chunks ? it dose like 3 chunks for 22 seconds video
Can i force it into more ?

Hi @kashkashio:

  • About how player can determine which resolution to use, check line 87 and 111. Line 87 we calculate the bandwidth should be used for the current resolution, line 111 we write that value into manifest file. Then when a player play your HLS, it change to the correct resolution based on your network bandwidth.
  • To split to more chunks, at line 19, change segment_target_duration to a smaller number, I set it to 10 seconds per chunk at the moment, that's why it produces 3 chunks for your 22 seconds video.
@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Aug 19, 2020

Hi can you add an example using fmp4?

Hi @0crypto0, I don't have enough time to look at fmp4 at the moment, you're welcome to take my script and modify your own

@kashkashio

This comment has been minimized.

Copy link

@kashkashio kashkashio commented Aug 19, 2020

You are a life saver ! Works great !
Tho i wonder for example how dose safari determines which resultion to take from the playlist?
By screen size? by network speed? latency etc... ?
P.S
How can i force it to split to even more chunks ? it dose like 3 chunks for 22 seconds video
Can i force it into more ?

Hi @kashkashio:

About how player can determine which resolution to use, check line 87 and 111. Line 87 we calculate the bandwidth should use for the current resolution, line 111 we write that value into manifest file.
To split to more chunks, at line 19, change segment_target_duration to a smaller number, I set it to 10 seconds per chunk at the moment, that's what it produces 3 chunks for your 22 seconds video.

Thank you ! <3

@mindjek07

This comment has been minimized.

Copy link

@mindjek07 mindjek07 commented Aug 31, 2020

Thanks you so much Script Works Great. 👍 🥇 🥇

what if i have already multi bitrate video. (i just don't want to convert because it's take too much time and i have 7000 videos to convert .)
240p.mp4
360p.mp4
480p.mp4
720p.mp4
1080p.mp4

i just want to convert (split) to m3u8 and make playlist.m3u8 (Master playlist)

how can i do that ?

@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Aug 31, 2020

Thanks you so much Script Works Great. 👍 🥇 🥇

what if i have already multi bitrate video. (i just don't want to convert because it's take too much time and i have 7000 videos to convert .)
240p.m3u8
360p.m3u8
480p.m3u8
720p.m3u8
1080p.m3u8

i just want to convert (split) to m3u8 and make playlist.m3u8 (Master playlist)

how can i do that ?

I'm not clear what you're saying? can your elaborate your question?

@mindjek07

This comment has been minimized.

Copy link

@mindjek07 mindjek07 commented Aug 31, 2020

I'm not clear what you're saying? can your elaborate your question?

sorry for my bad English :/

I already have multi quality videos like : 240p.mp4 , 480p.mp4 , 720p.mp4
i don't want to re-encode them , just want m3u8 and make playlist.m3u8

@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Aug 31, 2020

I'm not clear what you're saying? can your elaborate your question?

sorry for my bad English :/

I already have multi quality videos like : 240p.mp4 , 480p.mp4 , 720p.mp4
i don't want to re-encode them , just want m3u8 and make playlist.m3u8

For that I don't find any option from FFMPEG that can match your needs, I think you'll need to write script yourself, but I don't think it's hard because we only have small number of quality files for each video.

My idea, for each video, put all of it quality files to 1 folder, then for each quality(resolution) you generate a manifest file by its name. Eg: for 240p.mp4, first use FFMPEG to ready it length, for example 123 seconds, then we generate a 240p.m3u8 with value like following:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:123
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:123.000000,
240p_000.ts
#EXT-X-ENDLIST

As you can see we only have 1 row, Note above the TARGETDURATION and EXTINF should be same. You do that for all other qualities in same folder. After finishing, you generate a master playlist like following, and you're ready to go:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=400000,RESOLUTION=426x240
240p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360
360p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=842x480
480p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720
720p.m3u8

As you can see the master playlist may not change usually, if all your videos has same qualities.

My recommend is you should create a manifest template file, then you can just easily replace value inside it, Eg:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:<HLS_DURATION>
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:<HLS_DURATION>.000000,
<HLS_QUALITY>_000.ts
#EXT-X-ENDLIST
@mindjek07

This comment has been minimized.

Copy link

@mindjek07 mindjek07 commented Aug 31, 2020

My idea, for each video, put all of it quality files to 1 folder, then for each quality(resolution) you generate a manifest file by its name. Eg: for 240p.mp4, first use FFMPEG to ready it length, for example 123 seconds, then we generate a 240p.m3u8 with value like following:

thanks for reply and suggestion :) let me try

@mindjek07

This comment has been minimized.

Copy link

@mindjek07 mindjek07 commented Sep 1, 2020

My idea, for each video, put all of it quality files to 1 folder, then for each quality(resolution) you generate a manifest file by its name. Eg: for 240p.mp4, first use FFMPEG to ready it length, for example 123 seconds, then we generate a 240p.m3u8 with value like following:

Made php script for that. if anyone needed : https://gist.github.com/mindjek07/65a76310cb7c008fbfe2c11aa6748fc9

@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Sep 1, 2020

My idea, for each video, put all of it quality files to 1 folder, then for each quality(resolution) you generate a manifest file by its name. Eg: for 240p.mp4, first use FFMPEG to ready it length, for example 123 seconds, then we generate a 240p.m3u8 with value like following:

Made php script for that. if anyone needed : https://gist.github.com/mindjek07/65a76310cb7c008fbfe2c11aa6748fc9

Nice job 👯

@mindjek07

This comment has been minimized.

Copy link

@mindjek07 mindjek07 commented Sep 5, 2020

hello bro i ' m trying to add watermark in video.

First i remove line no 105
cmd+=" ${static_params} -vf scale=w=${widthParam}:h=${heightParam}"

Than add watermark filter in line no 53

# static parameters that are similar for all renditions
static_params=" -filter_complex 'movie=732.png[watermark];[watermark][0]scale2ref=350*iw/2160:42*iw/2160[wm][v];[wm]setsar=1[logo];[v][logo] overlay=10:10:enable=not(between'(t,360,420)')[watermark]' -map '[watermark]' -c:a aac -ar 48000 -c:v h264 -profile:v main -crf 19 -sc_threshold 0"

Result video convert to m3u8 but without watermark, can you help me out ?

@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Sep 6, 2020

to put water mark on your video, you need to specify your watermark as an input along with your video. So the command should look like:

ffmpeg -i video.mp4 -i watermark.png -filter_complex.... <other generate HLS options>

But if we do this, it'll break HLS generate command.

I have the same problem like yours before and my solution is: put watermark on video first, using separate ffmpeg command, then generate HLS after that.

# put a watermark on video with width=200px, height is autoscale with width, at position (x, y) = (15, 15)
ffmpeg -hide_banner -y -i video.mp4 -i watermark.mp4 -filter_complex "[1:v]scale=200:-1[wm];[0:v][wm]overlay=15:15" -crf 19 output

After that your just need to pass the output video to my script.

By doing this you remove the complexity when combining adding watermark with the HLS generate command, also make the water mark display in same position for any quality of the video (I was trying to combine them and resulted in the watermark jumps when quality change during playing)

@mindjek07

This comment has been minimized.

Copy link

@mindjek07 mindjek07 commented Sep 9, 2020

to put water mark on your video, you need to specify your watermark as an input along with your video. So the command should look like:

ffmpeg -i video.mp4 -i watermark.png -filter_complex.... <other generate HLS options>

But if we do this, it'll break HLS generate command.

I have the same problem like yours before and my solution is: put watermark on video first, using separate ffmpeg command, then generate HLS after that.

# put a watermark on video with width=200px, height is autoscale with width, at position (x, y) = (15, 15)
ffmpeg -hide_banner -y -i video.mp4 -i watermark.mp4 -filter_complex "[1:v]scale=200:-1[wm];[0:v][wm]overlay=15:15" -crf 19 output

After that your just need to pass the output video to my script.

By doing this you remove the complexity when combining adding watermark with the HLS generate command, also make the water mark display in same position for any quality of the video (I was trying to combine them and resulted in the watermark jumps when quality change during playing)

Yaap, thanks for the suggestion. instant of put watermark on a one video. I convert the video with watermark to multi bitrate (720p 480p 360p), then convert to m3u8 (save time).

Here is the code if anyone needed

for i in *.mp4; do mkdir -p "${i%.*}" && ffmpeg -i "$i" -hide_banner -y \
-i /watermark.png \
-filter_complex '[1][0]scale2ref=w='iw*15/100':h='ow/mdar'[wm][v];[v][wm]overlay=10:10,split=3[720p][480p][360p];
[720p]scale=w=1280:h=720:force_original_aspect_ratio=decrease[720p];
[480p]scale=w=842:h=480:force_original_aspect_ratio=decrease[480p];
[360p]scale=w=640:h=360:force_original_aspect_ratio=decrease[360p]' \
        -map '[720p]' -map 0:a -c:a copy -c:v libx264 -crf 27 ${i%.*}/720p.mp4 \
        -map '[480p]' -map 0:a -c:a copy -c:v libx264 -crf 27 ${i%.*}/480p.mp4 \
        -map '[360p]' -map 0:a -c:a copy -c:v libx264 -crf 27 ${i%.*}/360p.mp4 ; done
@kutanoor1988

This comment has been minimized.

Copy link

@kutanoor1988 kutanoor1988 commented Sep 29, 2020

I have a mkv video with multiple audio tracks one is in English and other in hindi. When i use this coe it works perfectly and convert videos in to multiple quality playlist but the problem is the output playlist contains only one audio track. Can you please assist me to how can i get all audio tracks from my original mkv file to converted m3u8 playlist

@JohnsonSnow

This comment has been minimized.

Copy link

@JohnsonSnow JohnsonSnow commented Oct 16, 2020

Thanks for putting this out. I'm currently working on a nodejs project that use dustjs as the templating engine, how do i generate hls manifest and dash manifest using the nodejs exec command to run shell script within a docker container?

@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Oct 17, 2020

Thanks for putting this out. I'm currently working on a nodejs project that use dustjs as the templating engine, how do i generate hls manifest and dash manifest using the nodejs exec command to run shell script within a docker container?

Here's my simple video transcoder server that's uses NodeJS (Express + Multer) and generate HLS using exec command, the project is dockerized and can run with Docker.

You'll need an Azure storage account in order to run but you may modify my code to remove that part if not needed

@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Oct 17, 2020

I have a mkv video with multiple audio tracks one is in English and other in hindi. When i use this coe it works perfectly and convert videos in to multiple quality playlist but the problem is the output playlist contains only one audio track. Can you please assist me to how can i get all audio tracks from my original mkv file to converted m3u8 playlist

This answer seems what you're looking for. The idea is quite simple:

  • Step 1: generate HLS
  • Step 2: generate audio track 1
  • Step 3: generate audio track 2
  • ....
  • Step n: generate audio track (n - 1)
@JohnsonSnow

This comment has been minimized.

Copy link

@JohnsonSnow JohnsonSnow commented Oct 17, 2020

Thanks for putting this out. I'm currently working on a nodejs project that use dustjs as the templating engine, how do i generate hls manifest and dash manifest using the nodejs exec command to run shell script within a docker container?

Here's my simple video transcoder server that's uses NodeJS (Express + Multer) and generate HLS using exec command, the project is dockerized and can run with Docker.

You'll need an Azure storage account in order to run but you may modify my code to remove that part if not needed

Thanks so much i really appreciate.

@JohnsonSnow

This comment has been minimized.

Copy link

@JohnsonSnow JohnsonSnow commented Oct 17, 2020

Thanks for putting this out. I'm currently working on a nodejs project that use dustjs as the templating engine, how do i generate hls manifest and dash manifest using the nodejs exec command to run shell script within a docker container?

Here's my simple video transcoder server that's uses NodeJS (Express + Multer) and generate HLS using exec command, the project is dockerized and can run with Docker.

You'll need an Azure storage account in order to run but you may modify my code to remove that part if not needed

One last thing any help on adding shaka packager to the project so i can generate Mpeg - Dash .mpd format?

@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Oct 18, 2020

Thanks for putting this out. I'm currently working on a nodejs project that use dustjs as the templating engine, how do i generate hls manifest and dash manifest using the nodejs exec command to run shell script within a docker container?

Here's my simple video transcoder server that's uses NodeJS (Express + Multer) and generate HLS using exec command, the project is dockerized and can run with Docker.
You'll need an Azure storage account in order to run but you may modify my code to remove that part if not needed

One last thing any help on adding shaka packager to the project so i can generate Mpeg - Dash .mpd format?

Sorry but this script is intended to generate HLS only

@abhinandan-chakraborty

This comment has been minimized.

Copy link

@abhinandan-chakraborty abhinandan-chakraborty commented Nov 13, 2020

Hi, I tried your script on my Ubuntu server with 1GB ram/1 core. At first, the script was just getting stuck in some frame during processing... then I added "-preset ultrafast" after which it started working perfectly. However, I am thinking about using libx265 for better compression without much loss of quality, so when I change the line 53 from:
static_params="-c:a aac -ar 48000 -c:v h264 -profile:v main -crf 19 -sc_threshold 0"
to
static_params="-c:a aac -ar 48000 -c:v libx265 -preset ultrafast -x265-params crf=20 -profile:v main -sc_threshold 0"
again the entire script just hangs in middle of the process. I don't understand if I did something wrong or if I need to add some additional params to support x265 encoding?

@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Nov 21, 2020

@abhinandan-chakraborty
At this time I don't have time to dive into this script and make it supports for x265 encoding, you're free to implement your own

@abhinandan-chakraborty

This comment has been minimized.

Copy link

@abhinandan-chakraborty abhinandan-chakraborty commented Nov 21, 2020

@maitrungduc1410
Hi, thanks, I increased the CPU and ram on my server and it started working perfectly fine.

@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Nov 21, 2020

@abhinandan-chakraborty

glad to hear that 👍

@throne1986

This comment has been minimized.

Copy link

@throne1986 throne1986 commented Dec 3, 2020

[libx264 @ 0x564aee9d0b80] [Eval @ 0x7fff77e8fcb0] Undefined constant or missing '(' in 'keyint_min'
[libx264 @ 0x564aee9d0b80] Unable to parse option value "-keyint_min"
[libx264 @ 0x564aee9d0b80] Error setting option g to value -keyint_min.
Error initializing output stream 0:0 -- Error while opening encoder for output stream #0:0 - maybe incorrect parameters such as bit_rate, rate, width or height
[aac @ 0x564aee8ec100] Qavg: 20749.709
[aac @ 0x564aee8ec100] 2 frames left in the queue on closing
[aac @ 0x564aee8eda40] Qavg: 20749.709
[aac @ 0x564aee8eda40] 2 frames left in the queue on closing
[aac @ 0x564aee9307c0] Qavg: 21247.820
[aac @ 0x564aee9307c0] 2 frames left in the queue on closing
[aac @ 0x564aee9061c0] Qavg: 21247.820
[aac @ 0x564aee9061c0] 2 frames left in the queue on closing
[aac @ 0x564aee937280] Qavg: 21247.820
[aac @ 0x564aee937280] 2 frames left in the queue on closing

can you please help me with this?

@maitrungduc1410

This comment has been minimized.

Copy link
Owner Author

@maitrungduc1410 maitrungduc1410 commented Dec 3, 2020

[libx264 @ 0x564aee9d0b80] [Eval @ 0x7fff77e8fcb0] Undefined constant or missing '(' in 'keyint_min'
[libx264 @ 0x564aee9d0b80] Unable to parse option value "-keyint_min"
[libx264 @ 0x564aee9d0b80] Error setting option g to value -keyint_min.
Error initializing output stream 0:0 -- Error while opening encoder for output stream #0:0 - maybe incorrect parameters such as bit_rate, rate, width or height
[aac @ 0x564aee8ec100] Qavg: 20749.709
[aac @ 0x564aee8ec100] 2 frames left in the queue on closing
[aac @ 0x564aee8eda40] Qavg: 20749.709
[aac @ 0x564aee8eda40] 2 frames left in the queue on closing
[aac @ 0x564aee9307c0] Qavg: 21247.820
[aac @ 0x564aee9307c0] 2 frames left in the queue on closing
[aac @ 0x564aee9061c0] Qavg: 21247.820
[aac @ 0x564aee9061c0] 2 frames left in the queue on closing
[aac @ 0x564aee937280] Qavg: 21247.820
[aac @ 0x564aee937280] 2 frames left in the queue on closing

can you please help me with this?

@throne1986

Can you let me know which version of FFMEG you're using? Which OS you're running on?

And if you can share your file for me to reproduce the error that would be great

@deman777

This comment has been minimized.

Copy link

@deman777 deman777 commented Feb 18, 2021

Hi @maitrungduc1410. Thank you for great script! Why resolution is 842x480 but by standard it is 854x480? from here https://support.google.com/youtube/answer/6375112?co=GENIE.Platform%3DDesktop&hl=en

@Laurent929

This comment has been minimized.

Copy link

@Laurent929 Laurent929 commented Mar 9, 2021

Thank you very much for this script. I use it because I want to stream a full-screen background video (10s video loop) on a static webpage.
My problem if I use the script as it is, is that I will have the video in very low quality (240p) when it starts. I tried to reduce the 'segment_target_duration' and I get better results but for some reason, I can never get more than 5 segments, even if I set 'segment_target_duration' to 1s (or 0.5s). Because of that I always have a low quality (240p) for 2 seconds, then 480p. When the video has finished loading the quality is 720p and then the loop restart at 240p. It never plays in 1080p.

I've also tried with 240p,360p and 480p removed (line 12, 13 and 14). It's better but I would like the video to load in 1080p as fast as possible, as it looks quite pixelated at 720p (it's a time-lapse with a lot of details). Also the loading sometimes 'freeze' on mobile (4G).

Is it possible to adjust some settings to have short segments (1s or less) or anything that could help me 'improve' the stream?
My goal would be to get a similar user experience as I get when I open a video on Youtube, where it starts almost instantly with good quality.

@AcidRain9

This comment has been minimized.

Copy link

@AcidRain9 AcidRain9 commented Apr 15, 2021

-----START GENERATING HLS STREAM-----
Cannot
open
create-vod-hls.sh: line 44: Cannot open libmwv206dec.so, libmwv206dec.so: cannot open shared object file: No such file or directory
Cannot open libmwv206dec.so, libmwv206dec.so: cannot open shared object file: No such file or directory
Cannot open libmwv206dec.so, libmwv206dec.so: cannot open shared object file: No such file or directory
128000: syntax error: invalid arithmetic operator (error token is ".so, libmwv206dec.so: cannot open shared object file: No such file or directory
Cannot open libmwv206dec.so, libmwv206dec.so: cannot open shared object file: No such file or directory
Cannot open libmwv206dec.so, libmwv206dec.so: cannot open shared object file: No such file or directory
128000")
create-vod-hls.sh: line 78: [: 128: unary operator expected
create-vod-hls.sh: line 90: [: open: integer expression expected
create-vod-hls.sh: line 98: (width / sourceWidth) * sourceHeight: division by 0 (error token is "sourceWidth) * sourceHeight")
Video source is too small

I'm having this error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment