Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
m3u8 stream to mp4 using ffmpeg

1. Copy m3u8 link

Alt text

2. Run command

echo "Enter m3u8 link:";read link;echo "Enter output filename:";read filename;ffmpeg -i "$link" -bsf:a aac_adtstoasc -vcodec copy -c copy -crf 50 $filename.mp4

Alt text

Example final command

ffmpeg -i "http://host/folder/file.m3u8" -bsf:a aac_adtstoasc -vcodec copy -c copy -crf 50 file.mp4
@xinshangshangxin
Copy link

xinshangshangxin commented May 18, 2018

if the url in m3u8 should with some header(like cookie), how to run it?

sometime some url thow 403, if I ue curl with header, it run successful

@amirasaran
Copy link

amirasaran commented May 25, 2018

Thanks

@gaojingwen945
Copy link

gaojingwen945 commented Aug 13, 2018

Nice approach~

@antidb
Copy link

antidb commented Sep 10, 2018

Thanks, it works on video from youtube.

@yuanchieh-cheng
Copy link

yuanchieh-cheng commented Sep 14, 2018

Is there any way to rerange the timestamp of segments in m3u8 list?

The segment is not continuous so I want to rewrite the timestamp.

error is like Non-monotonous DTS in output stream 0:0

@magicdawn
Copy link

magicdawn commented Jun 15, 2019

trying to understand what the options mean

ffmpeg -i "http://host/folder/file.m3u8" -bsf:a aac_adtstoasc -vcodec copy -c copy -crf 50 file.mp4

-bsf:a aac_adtstoasc

-c copy -vcodec copy

-crf 50

@shankarsundaram
Copy link

shankarsundaram commented Dec 9, 2019

you can use vlc to convert easily

@jcapinc
Copy link

jcapinc commented Jan 29, 2020

Tried using VLC and it was being tempermental on me, this worked so much better thank you

@suprnurd
Copy link

suprnurd commented Apr 8, 2020

Just what I was looking for, awesome! Thank you so much for sharing!

@cherkesgiller
Copy link

cherkesgiller commented Apr 8, 2020

Thank you so much, I appreciate it

@samelie
Copy link

samelie commented Apr 18, 2020

Works great on pbs.org since their single-page-app isn't understood by youtube-dl

@varenc
Copy link

varenc commented Jun 5, 2020

@magicdawn, most of the ffmpeg options here don't do anything so it's particularly confusing trying to understand them.

In most all situations you just need this:

ffmpeg -i "http://host/folder/file.m3u8" -c copy file.mp4

or

echo "Enter m3u8 link:";read link;echo "Enter output filename:";read filename;ffmpeg -i "$link"  -c copy $filename.mp4

to breakdown the original command...

-c copy -vcodec copy

is redundant. -c copy says "for all streams, do a stream copy" (no re-encoding). And then -vcodec copy says "for the video stream[s], do a stream copy". -c copy is all that's needed. You could also do -vcodec copy -acodec copy or -codec copy since -c is just a short version of -codec.

-crf 50

As you pointed out, since there's no transcoding going on, this video quality flag does nothing. And if it did work, 50 is such low quality that you probably wouldn't like the result. Also this flag is only supported by some codecs. (I wish ffmpeg gave you a warning when you pass in a useless flag!)

-bsf:a aac_adtstoasc

These days ffmpeg is smart enough to automatically insert this filter when needed. IMHO it's cleaner to just rely on that rather than always inserting it even when it's not necessary. (like in non-AAC+MPEG-TS streams) If you turn on verbose messaging with -v verbose you'll see that ffmpeg informs you of this:

$  ffmpeg -i https://...m3u8 -v verbose -c copy test.mp4
...
Automatically inserted bitstream filter 'aac_adtstoasc'; args=''

You can pull all this together and test things by downloading a stream with and without all these options and see that the resulting files are identical!

$ ffmpeg -i https://../stream.m3u8 -c copy test1.mp4
$ ffmpeg -i https://../stream.m3u8 -bsf:a aac_adtstoasc -vcodec copy -c copy -crf 50 test2.mp4
$ md5 test*.mp4
MD5 (test1.mp4) = 854e2beca4b3e516dd4cf147ca0521ee
MD5 (test2.mp4) = 854e2beca4b3e516dd4cf147ca0521ee

Another tip: if youtube-dl works but you want to control how ffmpeg downloads a stream without having to manually snoop for the .m3u8 file, just use youtube-dl's -g flag and it'll print out the m3u8 url and exit.

$ youtube-dl -g "https://host.com/some-webplay-video"

for example

$ youtube-dl -g https://www.pbs.org/video/east-bay-hip-hop-dance-in-richmond-ca-ng3v7e/
https://ga.video.cdn.pbs.org/videos/if-cities-could-dance/a7c8f330-2de4-4476-bb64-b23ae0bb2951/2000112404/hd-16x9-mezzanine-1080p/3f1sh5t6_richmond_iccd-16x9-1080p-1080p-6500k.m3u8

$ ffmpeg -i https://ga.video.cdn.pbs.org/videos/if-cities-could-dance/a7c8f330-2de4-4476-bb64-b23ae0bb2951/2000112404/hd-16x9-mezzanine-1080p/3f1sh5t6_richmond_iccd-16x9-1080p-1080p-6500k.m3u8 -c copy pbs-vid.mp4

@AshrayDesai
Copy link

AshrayDesai commented Jun 16, 2020

When I use this command:
ffmpeg -i "http://host/folder/file.m3u8" -c copy file.mp4

The resulting file.mp4 is always a video of different length -- sometimes 20min, sometimes, 15min, sometimes 5. Not only this, but the videos do not appear to start at the same time. Anyone know what could be happening?

@hmilz
Copy link

hmilz commented Jun 25, 2020

$ youtube-dl -g https://www.pbs.org/video/east-bay-hip-hop-dance-in-richmond-ca-ng3v7e/
https://ga.video.cdn.pbs.org/videos/if-cities-could-dance/a7c8f330-2de4-4476-bb64-b23ae0bb2951/2000112404/hd-16x9-mezzanine-1080p/3f1sh5t6_richmond_iccd-16x9-1080p-1080p-6500k.m3u8

$ ffmpeg -i https://ga.video.cdn.pbs.org/videos/if-cities-could-dance/a7c8f330-2de4-4476-bb64-b23ae0bb2951/2000112404/hd-16x9-mezzanine-1080p/3f1sh5t6_richmond_iccd-16x9-1080p-1080p-6500k.m3u8 -c copy pbs-vid.mp4

This is exactly what youtube-dl does if you just invoke

$ youtube-dl https://www.pbs.org/video/east-bay-hip-hop-dance-in-richmond-ca-ng3v7e/

i.e. it uses ffmpeg (or avconv if you so wish) in the background. No need to do that manually.

@ilovefood2
Copy link

ilovefood2 commented Sep 28, 2020

i got Output file #0 does not contain any stream error, not sure why

@tocamgar
Copy link

tocamgar commented Oct 26, 2020

I received this response from ffmpeg
image
Then I tried: youtube-dl --no-part --restrict-filenames --user-agent "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" "https://XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/playlist.m3u8?pulse=assetsXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
And it worked, then I tested the original comand line (ffmpeg) and now it worked without problems
Don't know why. 🤷‍♀️

@slavafomin
Copy link

slavafomin commented Apr 19, 2021

Also, the map option could be used to select specific program (group of streams / quality) from the m3u8 file: -map p:2.

@savinggrace
Copy link

savinggrace commented Jun 5, 2021

Tried using VLC and it was being tempermental on me, this worked so much better thank you

VLC interrupts the recording sometimes, but much more simple.

@pvonmoradi
Copy link

pvonmoradi commented Jun 22, 2021

@varenc thanks for this break-down. Wouldn't ffplay "$link" essentially do the same thing? (No need to use ffmpeg)

@pengyuwei
Copy link

pengyuwei commented Aug 18, 2021

How do I download m3U8 files that are encrypted using key? Such as:

#EXT-X-KEY:METHOD=AES-128,URI="https://xxxxx.com/somepath/somekey.key",IV=0x00000000000000000000000000000000

@varenc
Copy link

varenc commented Aug 19, 2021

@varenc thanks for this break-down. Wouldn't ffplay "$link" essentially do the same thing? (No need to use ffmpeg)

If you just want play the stream and watch it once, I agree it is! But then you might as well use mpv or VLC to play the stream. I figure people finding this gist want to download the stream and keep the media around for later viewing too.

@jokester
Copy link

jokester commented Oct 2, 2021

Thanks for the snippet, saved my day 👍

ffmpeg newbie here but, can I ask why -crf 50 was used when the encoder is copy ? Will it make a difference?

@varenc
Copy link

varenc commented Oct 2, 2021

ffmpeg newbie here but, can I ask why -crf 50 was used when the encoder is copy ? Will it make a difference?

You're right, it does nothing! Since there's no encoding going on and the stream is just being copied, -crf 50 has no effect. I broke this down more in my comment above.

This command is all you need: ffmpeg -i "http://host/folder/file.m3u8" -c copy file.mp4

@DaveFlashNL
Copy link

DaveFlashNL commented Apr 8, 2022

This command is all you need: ffmpeg -i "http://host/folder/file.m3u8" -c copy file.mp4

yes, but it only picked up the English audio in my stream just now, no subtitles, no other languages. how to fix that?

@Martyn575
Copy link

Martyn575 commented Aug 6, 2022

Does not work don't waste your time with garbage.

Fills my screen with millions of lines of garbage text. No closer to having anything usable.

Still trying to find alternate ways to convert this. Between censorship and youtube doing this, can't see any reason at all to use google products ever again.

@ibillabong
Copy link

ibillabong commented Aug 18, 2022

This worked for me - when ripping from other site - however I had the following error:
Protocol 'http' not on whitelist 'file,crypto,data'!

By adding "-protocol_whitelist file,http,https,tcp,tls" before "-i" fixed this error. So:
ffmpeg -protocol_whitelist file,http,https,tcp,tls,crypto -i [input] [flags] [output]

@ilyazub
Copy link

ilyazub commented Sep 9, 2022

This command is all you need: ffmpeg -i "http://host/folder/file.m3u8" -c copy file.mp4

Thanks! How to speed up the download (like, download concurrently?)

@varenc
Copy link

varenc commented Sep 9, 2022

This command is all you need: ffmpeg -i "http://host/folder/file.m3u8" -c copy file.mp4

Thanks! How to speed up the download (like, download concurrently?)

@ilyazub
For concurrently downloading you can't just use ffmpeg by itself. However, here are 2 things you can try:

Option 1

Use yt-dlp** or youtube-dl with aria2c as an external downloader. This just makes yt-dlp leverage aria2c for media downloading so you get concurrency.

$ yt-dlp --external-downloader aria2c --external-downloader-args '-c -j 8 -x 8 -s 8 -k 2M' "https://host/folder/file.m3u8"

Note the args I've set for aria2c in the above example. These tell aria2c to continue partial downloads, to use 8 parallel download requests, and to limit each request to 2MB each (-k 2M). Feel free to try tuning these parameters. For most situations limiting each download to 2M will slow things down but sometimes media hosts (dailymotion) apply traffic policies that allow the first part of a download stream to go very fast, but then they start rate limiting the download as it continues. By restarting each request after 2M you can sometimes subvert these limits.

Option 2

Use yt-dlp or youtube-dl to extract the relevant URLs and then use aria2c manually for concurrent downloading. Like this:

$  yt-dlp -g "https://host/folder/file.m3u8"  # the -g flag tells youtube-dl to just return the media url[s]
...
https://media.clip.host/..../media.mp4
$ aria2c "https://media.clip.host/..../media.mp4"

Warning: often a root .m3u8 references other .m3u8 files which then reference the media so you might need to follow the tree of references a few levels deep.

**These days I recommend yt-dlp over youtube-dl. It's just a fork but it seems more regularly updated and in general succeeds more often for me.

@tronghieu60s
Copy link

tronghieu60s commented Sep 22, 2022

I'm having a problem trying to decode when the player is using the fake img link, I see the video file has a ?PNG at the beginning of the content, anyone have a way to fix this?

File .m3u8

#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:7
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:6
https://s5.bestchill.workers.dev/sg/MjAyMjA5MjJfMjgg/MDAxNjYzODM5MTIyMjQz.2NlYD5uKQINWCu4tSseNg5wAEMPSpLqy1uPC7PsqwrAg.rPpnj1itZm2hbQWcxJ3uGzDHhzhWQDeirLrwZ4zrcHkg/index.html
#EXTINF:6
https://s1.bestchill.workers.dev/sg/MjAyMjA5MjJfMTM0/MDAxNjYzODM5MTIyMjA4.09L5NGYqCUWXn0Ff__jugCt3hzYxnovpjrvg0fG4SVIg.CXsKbhRODTttikxbAd0RQiQnvvWDAKtU_cNhlsieookg/index.html
#EXTINF:5.6
https://s3.bestchill.workers.dev/sg/MjAyMjA5MjJfNDgg/MDAxNjYzODM5MTM0OTc1.o-awTLVDuR2c1fV7bdavYODnsl7M0p5PCP05Q07fjiQg.8LIfxzp--TCKU3buOCkznGCGHMvnyabAbjvyH2pN0MEg/index.html
#EXTINF:6
...

File HLS index.html

�PNG � IHDR����ؿů pHYs�������� cHRMz%������u0�`:��o�_�F�IDATx�b,�����������
...

This is 2 file example:

https://github.com/tronghieu60s/tronghieu60s.github.io/blob/master/index.m3u8
https://github.com/tronghieu60s/tronghieu60s.github.io/blob/master/index.html

@ilyazub
Copy link

ilyazub commented Oct 3, 2022

@varenc Thank you the solution with yt-dlp and aria2c!

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