Log in

View Full Version : FFmpeg multi select and combine X% and Y chunk length


Selur
3rd March 2014, 10:49
What I want to do is open up a clip and select X% of it using Y seconds chunks with ffmpeg.

Looking around I found:
Well, you still can use the trim filter for that. Here is an example, lets assume that you want to cut out segments 30-40 sec (10 sec) and 50-80 sec (30 sec):
ffmpeg -i in.ts -filter_complex \
"[0:v]trim=duration=30[a]; \
[0:v]trim=start=40:end=50,setpts=PTS-STARTPTS[b]; \
[a][b]concat[c]; \
[0:v]trim=start=80,setpts=PTS-STARTPTS[d]; \
[c][d]concat[out1]" -map [out1] out.ts

What I did here? I trimmed first 30 sec, 40-50 sec and 80 sec to end, and then combined them into stream out1 with the concat filter.

About setpts: we need this because trim does not modify picture display time, and when we cut out 10 sec decoder counter does not see any frames for this 10 sec.

source: http://superuser.com/a/682534

which looks fine, problem is if I in example have a 7200 second clip and want to select 10% of the clip using 2second chunks I would have to extend this to handle
combined chunk size: 7200/100*10 = 720
number of chunks: 720 / 2 = 360
360 chunks with a distance of (720/360) 20 seconds between the start points of the sections.
Sure I could write some script/program which will create such a combined call, but that call would be crazy (totally impractical) large.

-> Does anyone know better way to do this than to create such a large call?


Cu Selur

Ps.: I would also like to do the same with mencoder. :D

Selur
3rd March 2014, 20:40
Got a general idea using ffmpeg and select.
Assuming I have a clip which is 1266s long and I want to analyse 5% of the file where each segment is 5 seconds long.
I could calculate beforehand:
maxChunkCount = length/chunkLength = 1266/5 = 253 chunks
neededChunkCount = 253*5/100 = 12
distanceBetweenChunks = length/neededChunkCount = 1266s/12 = 105s
Now I thought I could use:
-vf "select='lt(mod(n, distanceBetweenChunks), chunkLength)'
so in my case:
-vf "select='lt(mod(n, 105), 5)'
but looking at when calling:
ffplay -i "Path to file" -vf "select='lt(mod(n, 105), 5)' -an
the output doesn't seem to be what I want.
So my conclusion is that I don't really get how the select statement works.
-> Does anyone know how this call should be that it does what I want?

---
got a bit further since my input is progressive at 29.97fps and my output should be to, I needed to extend the vf-part to:
-vf select='lt(mod(n\, 105),8)',setpts='N/((30000/1001)*TB)'" this stops ffmpeg from adding unneeded dumplicates, problem is the not as I wanted, so any help is still appreciated.

Cu Selur

digitall.h
6th March 2014, 23:45
When I try in terminal
ffplay -i "Path to file" -vf select='lt(mod(n\, 105),8)',setpts='n/((30000/1001)*TB)'
ffplay exits and plays nothing.

Selur
7th March 2014, 00:09
try:
ffplay -i "Path to file" -vf "select='lt(mod(n\, 105),8)',setpts='N/((30000/1001)*TB)'" -an
and to not forget the replace the 'path to file' part :)

digitall.h
8th March 2014, 01:18
...and to not forget the replace the 'path to file' part :)

:scared::scared:
:sly:
:angry:
:devil:

:cool:
:D

will try again

Selur
13th March 2014, 11:30
Looked again at it and the numbers need to address frames not seconds. (http://www.ffmpeg.org/ffmpeg-filters.html#select_002c-aselect)

So looking at a clip with 125906 frames, a frame rate of 25fps and aiming for 5% using 5 second chunks, I get:
chunkSizeInFrames = outputFrameRate * "selection length in seconds" = 25 * 5 = 125
maxChunkCount = outputFrameCount / chunkSizeInFrames = 125906/125 = 1007
neededChunkCount = maxChunkCount * percent / 100 = 1007*5/100 = 50
distanceBetweenChunks = outputFrameCount / neededChunkCount = 125906/50 = 2518
-> I need to select the first 125 frames of every 2518 frames, thus my call should look like this:
ffplay -i "path to file" -vf "select='lt(mod(n\, 2518),125)',setpts='N/(25*TB)'" -an
Problem is, this still doesn't seem to be correct, since the chunks appear to be too small.

-> so I'm probably still overlooking something

okay, seems like when I use ffmpeg instead of ffplay it works like it should.

Selur
13th March 2014, 12:29
Anybody got an idea how to do this with mencoder?

digitall.h
18th March 2014, 17:24
ffplay -i "path to file" -vf "select='lt(mod(n\, 2518),125)',setpts='N/(25*TB)'" -an


Yes, this command works for me. :)

As you say, it looks strange with ffplay, but it seems to me that it plays the correct number of frames but at a higher framerate.
It will probably correctly feed the desired number of frames to the encoder.

I don't know what's the meaning of the little 'stops' of ffplay between chunks, I suppose that is waiting for the next chunk. I guess it will slow down the process a little, but it is better than playing the whole length.

Selur
19th March 2014, 10:40
Now I just need also a way to do this with mencoder. :)

digitall.h
19th March 2014, 21:37
With mencoder, I suppose you can encode a chunk combining -ss and -endpos. I don't know how you can repeat it for all the chunks needed to encode X% of the file.
:o

Selur
19th March 2014, 21:39
That is the problem, encoding one chunk isn't the problem, encoding multiple is. ;)

digitall.h
3rd August 2014, 20:45
I'm sorry to bump this thread, almost 5 months after last post.

In last x264-r2453-ea0ca51 version I read in the help:
Filtering:

--vf, --video-filter <filter0>/<filter1>/... Apply video filtering to the input file

Filter options may be specified in <filter>:<option>=<value> format.

Available filters:
...
select_every:step,offset1[,...]

This filter was probably there before, but I had not seen it.
Do you think we could use it for the purpose of the topic of this thread?.
Of course it is not the answer to your question, but it could help to solve the 'select-range-every' question in x264... or not.

Selur
3rd August 2014, 20:57
using it in x264 doesn't really help, since then the whole file would have to be fully decoded, which would probably take quite some time, also it basically looks like the normal 'select' in ffmpeg

Selur
3rd August 2014, 20:57
don't think it will work, if I just had tons of time I could test the ffmpeg options more,.. :)

digitall.h
6th August 2014, 00:10
Well, it worked... in a way.

I tried this command:
"/home/ffmpeg_build/ffmpeg" -y -v -10 -r 25 -analyzeduration 100M -probesize 100M -i "/home/tests/DVDtest/VTS_09_1.VOB" -an -sn -threads 4 -vf crop=720:560:0:8 -vsync 0 -r 25 -pix_fmt yuv420p -f rawvideo - | "/home/x264_bins/x264-r2453-ea0ca51" --preset veryslow --tune film --crf 20 --profile high --level 4.1 --ref 4 --aq-mode 2 --vbv-maxrate 62500 --vbv-bufsize 78125 --output-csp i420 --fps 25 --input-res 720x560 --video-filter select_every:1221,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24 --output "/home/TMP/test.264" -
I made calculations to encode 2% of 23203 frames, with chunks of 1 second length (video is 25 fps). But this did not work, it encoded a small black video file.
:(

I then tried:
"/home/x264_bins/x264-r2453-ea0ca51" --preset veryslow --tune film --crf 20 --profile high --level 4.1 --ref 4 --aq-mode 2 --vbv-maxrate 62500 --vbv-bufsize 78125 --video-filter select_every:1221,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24 --output "/home/tests/DVDtest/VTS_09_1.VOB"
And it worked!.
:D
It encoded 478 frames, at 5.90 fps and 144.48 kb/s, and it took 1m21.408s.

I then encoded the VOB file with the same command line, eliminating the --video-filter select_every... part.
And it encoded 23202 frames, at 19.13 fps and 1103.58 kb/s, and it took 20m12.809s

So the --video-filter slows down the encoding, as you had predicted.
At the same speed it encoded the whole VOB with no --video-filter, 2% of the same VOB would have been encoded in just 24.256s, faster than 1m21.408s with --video-filter.
And at the speed it encoded 2% of the VOB with --video-filter, it would have encoded the 23203 frames VOB in 1h05m51.524s
:scared:

It's slower. But it's better than encoding the whole video and realize that the file is too big and you have to reencode.
Do you think this can help in any way?.

Selur
6th August 2014, 06:11
Nope.
a. it would only be a solution for x264
b. not each x264 version comes with vf
c. seems simply to slow
-> like I wrote before, I need to get this working with ffmpeg

digitall.h
6th August 2014, 20:24
You're right it is slower.

So I lost my time...

But I cannot help with ffmpeg.
You pointed out some directions, I managed to follow.
But I'm not able to make improvements.
I suppose you already asked in ffmpeg support forum, with no result.
:(

Selur
6th August 2014, 20:27
Which ffmpeg support forum? (afaik there is only a irc channel and a mailing list for support)

digitall.h
6th August 2014, 21:00
That's it, support channels.
No luck?

Selur
6th August 2014, 21:02
No time and motivation. :)

Selur
13th August 2014, 08:28
Had a few thoughts, sticking with the way ffmpeg does it atm. (frame count seems to be off a bit) and ignoring vfr input, one would calculate:
chunkLengthInSeconds=roundDown(LengthOfClip/1000)
chunkSizeInFrames = clipFrameRate * chunkLengthInSeconds
maxChunkCount = clipFrameCount / chunkSizeInFrames[/code]
neededChunkCount = maxChunkCount * X% / 100[/code]
distanceBetweenChunks = clipFrameCount / neededChunkCount[/code]
and then depending on the decoder ffmpeg/mencoder/avisynth use:

ffmpeg:
ffmpeg -i "path to file" -vf "select='lt(mod(n\, distanceBetweenChunks),chunkSizeInFrames)',setpts='N/(outputFrameRate*TB)'" -an -sn "PATH to output"

mencoder:
Create an EDL file which contains all the chunks you want to encode

...
[begin second] [end second] 1
...

sadly I couldn't find a better way.

Avisynth:
Select(chunkSizeInFrames, distanceBetweenChunks-chunkSizeInFrames)

Cu Selur

digitall.h
26th August 2014, 22:36
I'm using regularly the ffmpeg code, and it works well.
It gives a good estimation of what will be the final size of the video file.
:D