Is it possible to mute a section of a video file (say 5 seconds) without having to re-encode the whole audio stream with ffmpeg? I know it's technically (though probably not easily) possible by reusing the majority of the existing audio stream and only re-encoding the changed section and possibly a short section before and after, but I'm not sure if ffmpeg supports this. If it doesn't, anyone know of any other library that does?
You can do the partial segmented encode, as you suggest, but if the source codec is DCT-based such as AAC/MP3, there will be glitches at the start and end of the re-encoded segment once you stitch it all back together.
You would use the segment muxer and concat demuxer to do this.
ffmpeg -i input -vn -c copy -f segment -segment_time 5 aud_%d.m4a
Re-encode the offending segment, say aud_2.m4a to noaud_2.m4a.
Now create a text file
file aud_0.mp4
file aud_1.mp4
file noaud_2.mp4
file aud_3.mp4
and run
ffmpeg -an -i input -f concat -safe 0 -i list.txt -c copy new.mp4
Download the small sample file.
Here is my plan visualized:
# original video
| video |
| audio |
# cut video into 3 parts. Mute the middle part.
| video | | video | | video |
| audio | | - | | audio |
# concatenate the 3 parts
| video | video | video |
| audio | - | audio |
# mux uncut original video with audio from concatenated video
| video |
| audio | - | audio |
Let's do this.
Store filename:
i="fridayafternext_http.mp4"
To mute the line "What the hell are you doing in my house!?", the silence should start at second 34 with a duration of 2 seconds.
Store all that for your convenience:
mute_starttime=34
mute_duration=2
bash supports simple math so we can automatically calculate the start time where the audio starts again, which is 36 of course:
rest_starttime=$(( $starttime + $duration))
Create all 3 parts. Notice that for the 2nd part we use -an to mute the audio:
ffmpeg -i "$i" -c copy -t $mute_starttime start.mp4 && \
ffmpeg -i "$i" -ss $mute_starttime -c copy -an -t ${mute_duration} muted.mp4 && \
ffmpeg -i "$i" -ss $rest_starttime -c copy rest.mp4
Create concat_videos.txt with the following text:
file 'start.mp4'
file 'muted.mp4'
file 'rest.mp4'
Concat videos with the Concat demuxer:
ffmpeg -f concat -safe 0 -i concat_videos.txt -c copy muted_audio.mp4
Mux original video with new audio
ffmpeg -i "$i" -i "muted_audio.mp4" -map 0:v -map 1:a -c copy "${i}_partly_muted.mp4"
Note:
I've learned from Gyan's answer that you do the last 2 steps in 1 take which is really cool.
ffmpeg -an -i "$i" -f concat -safe 0 -i concat_videos.txt -c copy "${i}_partly_muted.mp4"
Related
there are what i done:
download a full mp4 file.
due to it's watermark(0s-10s), i split the full video into 2 parts from 10second. the first part with watermark.
use ffmpeg delogo the first part.
merge the two video into a full again.
wget -O download.mp4
ffmpeg -i download.mp4 -vcodec copy -acodec copy -t 00:00:10 tmp1.mp4
ffmpeg -i download.mp4 -vcodec copy -acodec copy -ss 00:00:10 tmp2.mp4
ffmpeg -i tmp1.mp4 -vf "delogo=x=432:y=44:w=1060:h=108" -c:a copy tmp3.mp4
echo file tmp3.mp4 > mergelist.txt && echo file tmp2.mp4 >> mergelist.txt
ffmpeg -f concat -i mergelist.txt -c copy output.mp4
problem i faced:
in the last merged video, only one tmp part is fine, the other's video and voice not sync and play time more faster than before.
why i divide it, delogo(although only the first 10 seconds shows) full video more than 1h, re-encode takes much time, 10s part fine to me.
Have you tried this?
ffmpeg -i download.mp4 -vf "delogo=enable='lte(t,10)':x=432:y=44:w=1060:h=108" -c:a copy output.mp4
I'm assuming delogo filter is timeline editing enabled.
I can do soxi -d * to get audio length information in hours, minutes, and seconds.
However it would only give me info on the individual audio length.
If I wanted to see the audio length for the entire folder, how can I accomplish such task?
like when you do "wc -w" it shows the sum of everything at the end. Is there a flag or something I can integrate with soxi?
soxi -T *
from the man file
-T Used with multiple files; changes the behaviour of -s, -d and -D to
display the total across all given files. Note that when used with -s with files > with different sampling rates, this is of questionable value.
Assume you have .wav files in directory.
For single file use soxi -D ..., and then sum values with bc (how to)
Use any of this commands to get total in seconds:
# in seconds
soxi -D *.wav | awk '{s+=$1}END{print s}' | bc
# in seconds
soxi -D *.wav | paste -sd+ - | bc
# in minutes
soxi -D *.wav | awk '{s+=$1}END{print s/60}' | bc
# in hours
soxi -D *.wav | awk '{s+=$1}END{print s/60/60}' | bc
does it have to be soxi ? if you're willing to use ffprobe as part of ffmpeg, here's how it scanned a folder of mine with different file types - .mp3, AAC in .m4a, and FLAC :
making the lazy assumption that your filenames don't contain the equal sign ("=") in their names. if they do, adjust the csv=s= option accordingly
gfind . -type f -not -name ".*" -print0 |
parallel -0 --bar -N 1 -j 8
'ffprobe -hide_banner -v 0
-select_streams a:0
-show_entries format=format_long_name,size,filename,duration
-of csv=s="=":p=0:nk=0
-i {}'
./genieaudio_93508443_.lossless.mp3
MP2/3 (MPEG audio layer 2/3)
232.150204
9287144
./genieaudio_16277926_.aac.flac.m4a
QuickTime / MOV
232.181000
63572859
./genieaudio_16277926_.lossless.mp3
MP2/3 (MPEG audio layer 2/3)
232.280816
92923682/3)
250.096327
10004990
./genieaudio_79412303_.lossless.mp3
MP2/3 (MPEG audio layer 2/3)
250.383673
10016483
./genieaudio_16108705_.192k.mp3.flac
raw FLAC
251.122000
55480793
./backupgenieaudio_16108705_test1.192k.mp3
MP2/3 (MPEG audio layer 2/3)
251.928000
6046272
./genieaudio_16108705_test1.192k.mp3
MP2/3 (MPEG audio layer 2/3)
251.928000
6046893
./genieaudio_16108705_test2.192k.mp3
MP2/3 (MPEG audio layer 2/3)
251.928000
6046848
./genieaudio_16254360_192_b.mp3
MP2/3 (MPEG audio layer 2/3)
255.111837
6123354
./genieaudio_16268888_.192k.mp3.flac
raw FLAC
259.442979
55115022
I'm using Spleeter to remove music from audios.
My goal is to build a script that automates the process of extracting audio from the video, execute Spleeter on the extracted audio & than merge the manipulated audio back to the video replacing the original one.
The main issue I had is that I don't have enough ram to process the whole extracted audio. I need to split it the into multiple pieces & execute Spleeter upon each piece.
Then concatenate the manipulated pieces together and merge the result to the video.
Here's what I tried:
#!/bin/bash
cd ~/Desktop/Video-convert
# create audio from video
ffmpeg -i *.mp4 output.mp3
# Split the audio into pieces
ffmpeg -i output.mp3 -f segment -segment_time 120 -c copy output_%03d.mp3
# Execute Spleeter upon each sample
FILES=~/Desktop/Video-convert/*.mp3
for f in $FILES
do
spleeter separate -i $f -o output_vocal
done
# delete unneeded audios
rm *.mp3
cd output_vocal
# ===========================================================
# the problem starts here
# ===========================================================
# concatenate manipulated audios together
find . -name 'vocals.wav' -exec echo {} >> mylist.txt \;
ffmpeg -f concat -safe 0 -i mylist.txt -c copy vocal.mp3
mv vocal.mp3 ../
cd ../
# merge the audio back to video
ffmpeg -i *.mp4 -i vocal.mp3 \
-c:v copy -c:a aac -strict experimental \
-map 0:v:0 -map 1:a:0 vocal-vid.mp4
Everything works well until having to concatenate the audios together. Spleeter outputs the result into vocal.wav & accompaniment.wav within a sub-folder that is named the same as the audio that was processed.
The File Tree looks like this:
output_vocal
- output_000
----- vocal.wav
----- accompaniment.wav
- output_001
----- vocal.wav
----- accompaniment.wav
- output_002
----- vocal.wav
----- accompaniment.wav
As you can see the problem comes with the naming. My objective is to concatenate all vocal.wav into one mp3 audio.
And then merge the final vocal.mp3 audio with the *.mp4 video.
Only issue is going around the way that Spleeter outputs the result audios.
the problem you are experiencing is that ffmpeg's concat demuxer requires an input file that contains directives, rather than a naive file-list.
Your find invocation creates a file like:
output_vocal/output_000/vocal.wav
output_vocal/output_001/vocal.wav
output_vocal/output_002/vocal.wav
whereas ffmpeg's concat demuxer really requires a file like:
file output_vocal/output_000/vocal.wav
file output_vocal/output_001/vocal.wav
file output_vocal/output_002/vocal.wav
Also note that find does not necessarily return the files in alphabetic order, whereas you will most likely want to concatenate the files in that order.
Finally, when concatenating the WAV-files, you cannot use the copy codec to generate an MP3 file (since the WAV/RIFF codec is not MP3). but you don't need an intermediate MP3-file anyhow
Here's an updated script, that
- uses a temporary directory for all intermediate files
- iterates over all mp4-files provided at the cmdline (rather than hardcoding the input directory)
- creates a "XXX_voc.mp4" file for each input file "XXX.mp4" (overwriting any existing files)
#!/bin/bash
for infile in "$#"
do
outfile=${infile%.mp4}_voc.mp4
# create a temp-directory to put our stuff to
TMPDIR=$(mktemp -d)
# create audio from video
ffmpeg -i "${infile}" "${TMPDIR}/output.mp3"
# Split the audio into pieces
ffmpeg -i "${TMPDIR}/output.mp3" -f segment -segment_time 120 -c copy "${TMPDIR}/output_%03d.mp3"
# Execute Spleeter upon each sample
find "${TMPDIR}" -maxdepth 1 -type f -name "output_*.mp3" \
-exec spleeter separate -i {} -o "${TMPDIR}/output_vocal" ";"
# find all 'vocal.wav' files generated by spleeter, sort them,
# prefix them with 'file ', and put them into output.txt
find "${TMPDIR}/output_vocal" -type f -name "vocal.wav" -print0 \
| sort -z \
| xargs -0 -I{} echo "file '{}'" \
> "${TMPDIR}/output.txt"
# concatenate the files and create an MP3 file
ffmpeg -f concat -safe 0 -i "${TMPDIR}/output.txt" -c copy "${TMPDIR}/vocal.wav"
# merge the audio back to video
ffmpeg -y -i "${infile}" -i "${TMPDIR}/vocal.wav" \
-c:v copy -c:a aac -strict experimental \
-map 0:v:0 -map 1:a:0 "${outfile}"
rm -rf "${TMPDIR}"
done
I am passing a processed video from openCV to ffmpeg via a pipe here is the code
./OpenCV & \
tail -n +0 -f out.avi | ffmpeg -i pipe:0 -hls_time 1 -hls_list_size 0 -hls_wrap 10 -hls_segment_filename '%03d.ts' stream.m3u8
My issue is the output .ts files are not in a uniformed duration they change from file to file.
These are mostly long say 60 seconds. This means the connecting client has to wait for the first stream to finish before the playlist file (.m3u8) file is created. Therefor in this example they are 60 seconds or so behind live video and if the next .ts file is larger the streaming stops until this is finished. If the client tries to play before the next .ts file is created they are played the first .ts file.
The frame rate from openCV is 1 frame per second.
tail changes the output file of openCV called (out.avi) to a stdout.
Any help would be great.
I know I am answering my own question but I have changed the code below
./OpenCV & \
tail -n +0 -f out.avi | ffmpeg -i pipe:0 -hls_time 1 -hls_list_size 0 -hls_wrap 10 -hls_segment_filename '%03d.ts' stream.m3u8 `
to
./OpenCV & \
tail -n +0 -f out.avi | ffmpeg -i pipe:0 -f hls -g 2 -hls_time 2 -hls_list_size 0 -hls_wrap 10 -hls_segment_filename '%03d.ts' stream.m3u8 `
Seems to have done the trick.
Trying to record my desktop and also audio with RHEL6.
I'm using the command below, but the quality of the video output is not good.
It is very blurry and I can bearly make out text on screen.
The audio is good so no issues there.
Does anyone know how the make the video quality any better?
ffmpeg -f alsa -ac 2 -i hw:0,0 -f x11grab -s $(xwininfo -root | grep 'geometry' | awk '{print $2;}') -r 25 -i :0.0 -sameq -f mpeg -ar 48000 -s wvga -y sample.avi
I believe the -sameq option means 'same quantizer' not 'same quality' and is depreciated, see here.
Try -q 1 instead.
q being quality 1-32 (1 being highest)