99

I've got a bunch of ts segments described by a single index.m3u8 file:

index.m3u8        
segment1_0_av.ts  
segment2_0_av.ts  
segment3_0_av.ts  
segment4_0_av.ts  
segment5_0_av.ts

I know they are all encoded the same way. ffprobe gives me the following:

Input #0, mpegts, from 'segment1_0_av.ts':
  Duration: 00:00:10.00, start: 0.100511, bitrate: 1251 kb/s
  Program 1 
    Stream #0:0[0x100]: Video: h264 (Main) ([27][0][0][0] / 0x001B), yuv420p, 960x540 [SAR 1:1 DAR 16:9], 12.50 fps, 25 tbr, 90k tbn, 25 tbc
    Stream #0:1[0x101]: Audio: aac ([15][0][0][0] / 0x000F), 44100 Hz, stereo, fltp, 105 kb/s
    Stream #0:2[0x102]: Unknown: none ([21][0][0][0] / 0x0015)

I'd like to combine them into a single mp4 container. But when I try commands like:

ffmpeg -f concat -i filelist.txt -c copy output.mp4

where the generate the filelist.txt from the index.m3u8 file, it complains about not being able to read various files. But converting the ts files themselves seem to work fine. I think I'm not using ffmpeg properly.

How do I use ffmpeg to combine the ts files described by index.m3u8 into a single mp4 container using the copy codec?

0

9 Answers 9

116

I'm not sure why ffmpeg is giving you an error. However ts is one of the few formats that can simply be concatenated. Then, once you have a single ts, transmux to mp4.

Under windows:

copy /b segment1_0_av.ts+segment2_0_av.ts+segment3_0_av.ts all.ts
ffmpeg -i all.ts -acodec copy -vcodec copy all.mp4

Under GNU/Linux, using bash:

cat segment1_0_av.ts segment2_0_av.ts segment3_0_av.ts > all.ts
ffmpeg -i all.ts -acodec copy -vcodec copy all.mp4
2
63

Using copy or cat to combine the files like szatmary's current top answer might leave you with a file that plays far past the limit and can't seek along with playback issues.

Instead, to combine these files properly use ffmpeg as instructed in https://trac.ffmpeg.org/wiki/Concatenate. (Install ffmpeg here if you don't already have it https://github.com/adaptlearning/adapt_authoring/wiki/Installing-FFmpeg.)

If you're too lazy to read my first link, you basically have to create a .txt file listing all the files you want to combine like so (which my first link gives instructions on how to do easily) in the folder where you're doing the concatenation:

file '/path/to/file1'
file '/path/to/file2'
file '/path/to/file3'

Here's a copy paste from my first link on one way to create a text file if you have Windows on commandline for instance but obviously you can make the file manually or however you want:

(for %i in (*.ts) do @echo file '%i') > mylist.txt

Double check that your .txt file looks good and is formatted correctly!

After this, on commandline run:

ffmpeg -f concat -i mylist.txt -c copy all.ts

where 'mylist.txt' is the .txt file you just made.

Check if the resultant file plays video correctly. From here, you can transmux to mp4 as usual if you like:

ffmpeg -i all.ts -acodec copy -vcodec copy all.mp4
7
  • 1
    That for %i in (*.ts) do doesn't sort correctly for i >= 10. Apart from that, everything works, thanks. Indeed, there were problems with the concatenated file, not to mention that the file made using your method has turned out to be 16% smaller (I had 1400 parts by 400kb).
    – user
    Mar 24, 2019 at 6:20
  • @user for numbers in order you can iterate through numbers like for i in {1..10}; do echo file \'$i.ts\' >> list.txt ; done
    – qwr
    Nov 13, 2019 at 3:21
  • 4
    For me this causes an audio stutter at each join point, whereas the accepted answer doesn't. The resulting file is also marginally larger with this method.
    – markshep
    Jun 1, 2020 at 22:52
  • @markshep Did you find any solution for the audio stutter/popping sound? Jun 15, 2021 at 8:14
  • 1
    Depending on the source, rather than concatenating with ffmpeg, do it via cat (e.g. cat *.ts >> all.ts in Linux or copy /b *.ts all.ts in Windows) then doing the transmux step. When I did that, my audio glitches went away. (And of course it does depend on the file naming as to whether the wildcard will combine the files in the proper order. If nothing else, concatenate the binary data manually.) Jan 29, 2022 at 10:52
28

Putting all together

Using Juan Aquino's answer (and correcting the first command to be compatible with Bash and using the natural ordering of files), plus 7vujy0f0hy's answer, a simple working script for a Linux Bash shell is:

#!/bin/bash
for i in `\ls *.ts | sort -V`; do echo "file '$i'"; done >> mylist.txt
ffmpeg -f concat -i mylist.txt -c copy -bsf:a aac_adtstoasc video.mp4
2
  • 1
    Thanks for the sort part. That's what I was missing.
    – ttt
    Dec 30, 2021 at 11:12
  • 1
    This solution saves the manual labor of needing to list out every file by hand, and I didn't experience any audio stuttering when I tried this method. It works well! Dec 16, 2022 at 8:09
20

The correct way to concat multiple video files from m3u8 playlist is

ffmpeg -i "index.m3u8" -codec copy output.mp4


  • the m3u8 playlist can be on web or locally in directory
    • it contains list of file paths relative to the playlist
  • -codec copy to avoid encoding
  • container type matters:
    • *.mp4 is fine but it seems little slow to mux when playlist is fetched from web
    • *.mkv or *.ts worked best for me
1
  • 1
    For me this is the best way to download and combine ts files from m3u playlist. Note that in some cases you can have a "Protocol 'https' not on whitelist 'file,crypto,data'!" error. In this case simply put the option -protocol_whitelist file,http,https,tcp,tls,crypto BEFORE the -i file.m3u8
    – Antonio
    Apr 20, 2023 at 14:09
9

2017 answer

But when I try commands like ..., it complains about not being able to read various files.

When I execute ffmpeg -i some.ts -c copy some.mp4 on a certain video, I get this error message:

Malformed AAC bitstream detected: use the audio bitstream filter 
'aac_adtstoasc' to fix it ('-bsf:a aac_adtstoasc' option with ffmpeg)

av_interleaved_write_frame(): Operation not permitted

Not surprisingly, executing ffmpeg -i some.ts -c copy -bsf:a aac_adtstoasc some.mp4 fixes it.

1
  • 7
    Get a new enough ffmpeg and it will now do this automatically.
    – llogan
    Nov 11, 2017 at 18:40
7

You can use pipe these ts files in to ffmpeg and output the mp4 file.

cat *.ts | ffmpeg -i pipe: -c:a copy -c:v copy output.mp4

or If you ts file name not have order,

grep .*.ts index.m3u8 | xargs cat | ffmpeg -i pipe: -c:a copy -c:v copy output.mp4

4

You can do the concatenating simple like so (with bash):

for f in ./{0..<number>}.ts; do cat $f >> out.ts; done

Replace <number> with the highest number (obviously). The variants with ffmpeg didn’t work properly. The output video file would stutter weirdly.

I use the for loop to ensure the correct order of the files. Maybe you don’t need it. Maybe it’s even possible to pipe the output to ffmpeg and convert it to mp4 on the fly.

1

All the popular answers to this question that mislead readers to concatenate the TS files before running ffmpeg are incorrect. To ensure the audio and video do not fall out of sync during the assembly of the mp4 stream, the poorly documented but important "-f concat" feature of ffmpeg should be used.

    delimiterBeforeFileNumber="-"
    ls |egrep '[.]ts$' \
        |sort "-t$delimiterBeforeFileNumber" -k2,2n \
        |sed -r "s/(.*)/file '\1'/" >ts.files.txt

    ffmpeg -f concat -i ts.files.txt -c copy tsw.014.ts.mp4

The two preparatory lines of code just create a file containing a list of TS files in this line format:

    file 'seg-37-a.ts'
1
  • 1
    Actually, when I tried this, there was a slight "stutter" between the segments in the final video. So at least in that one case it helped concatenating the TS files first.
    – Sphinxxx
    Jul 31, 2019 at 18:44
0

Adding to @Juan's answer here https://superuser.com/a/1162353/1847419 You can use the following command in your terminal (any OS) if you have nodejs installed to handle sorting with numbers correctly.

node -e "require('fs').writeFileSync('all.txt', require('fs').readdirSync('.') .filter(f => f.endsWith('.ts')) .sort((a, b) => { const [ name1, seq1, _1, ] = a.split(/-|\./); const [ name2, seq2, _2, ] = b.split(/-|\./); if (name1 == name2) { return seq1 - seq2 } else { return name1.localeCompare(name2) } }).map(f => `file '${f}'`).join('\n'))"

Note: assumes your file name separates numbers by a '-' symbol like:

filename-1.ts filename-2.ts ...

You could then run the following to join the videos

ffmpeg -f concat -i mylist.txt -c copy all.ts

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .