How to make FFmpeg automatically inject mp3 audio tracks in the single cycled muted video? - audio

everybody here! So basically this is what I want to achieve:
I have a muted video about 3 minutes long.
I have a list of audio tracks in mp3 format (40 songs in a folder with duration 2 to 6 mins each one)
I want this video to play cycled automatically taking songs from playlist and injecting them to the video one by one. Every time a song finishes the next one from the list should start playing at the moment. Video continues playing and doesn't care duration of tracks.
I consider it as the first step on the way to broadcast radio with a video background on youtube in 24/7 mode with ability to put additional tracks to playlist without need to stop translation.
My problem is that I'm new in FFmpeg and I would appreciate any suggestions regarding which FFMpeg topic to start investigate with in order to achieve my goal

Use the concat demuxer
You can do live updates to the playlist for the concat demuxer, but each audio file must have the same attributes, the same number of streams, and all be the same format.
Create input.txt containing:
ffconcat version 1.0
file 'audio1.mp3'
file 'audio2.mp3'
file 'audio3.mp3'
file 'audio40.mp3'
All file names must be "safe" or it will fail with Unsafe file name. Basically no special characters in file names and only use absolute paths. See concat demuxer for more info.
Run ffmpeg to stream to YouTube:
ffmpeg -re -framerate 10 -loop 1 -i image.jpg -re -f concat -i input.txt -map 0:v -map 1:a -c:v libx264 -tune stillimage -vf format=yuv420p -c:a aac -g 20 -b:v 2000k -maxrate 2000k -bufsize 8000k -f flv rtmp://youtube
When you are ready to add new songs make temp.txt containing:
ffconcat version 1.0
file 'audio41.mp3'
file 'audio42.mp3'
file 'audio43.mp3'
Replace input.txt atomically:
mv temp.txt input.txt
See FFmpeg Wiki: Concatenate for lots more info.
If your audio files are not the same
The files listed in input.txt must all have the same:
Format (AAC, MP3, etc, but not mixed)
Sample rate (48000, 44100, etc)
Number of channels (mono, stereo, etc).
If they vary then you will have to pre-process them before adding them to the playlist. Bash example conforming each audio to stereo (-ac 2) with 44100 sample rate (-ar 44100) and save as AAC format in M4A container:
mkdir conformed
for f in *.mp3; do ffmpeg -i "$f" -map 0:a -ac 2 -ar 44100 -c:a aac "conformed/${f%.*}.m4a"; done
Outputting to AAC is recommended for streaming to YouTube.
If you do this then you can avoid re-encoding the audio in the ffmpeg command to YouTube. Just change -c:a aac to -c:a copy in step #2: Run ffmpeg to stream to YouTube.

Related

How to take metadata from .mp3 file and put it to a video as a text using FFmpeg?

In my previously opened topic:
How to make FFmpeg automatically inject mp3 audio tracks in the single cycled muted video
I've got detailed explanation from #llogan how to broadcast looped short muted video on youtube automatically injecting audio tracks in it without interrupting a translation.
I plan to enhance the flow and the next question I faced with is how to dynamically put an additional text to the broadcast.
Prerequisites:
youtube broadcast is up and running by ffmpeg
short 3 min video is paying in infinity loop
audio tracks from playlist are automatically taken by "ffmpeg concat" and injected in the video one by one
this is a basic command to start translation:
ffmpeg -re -fflags +genpts -stream_loop -1 -i video.mp4 -re -f concat
-i input.txt -map 0:v -map 1:a -c:v libx264 -tune stillimage -vf format=yuv420p -c:a copy -g 20 -b:v 2000k -maxrate 2000k -bufsize
8000k -f flv rtmp://a.rtmp.youtube.com/live2/my-key
Improvements I want to bring
I plan to store some metadata in audio files (basically it's an artist name and a song name)
At the moment a particular song starts playing artist/song name should be taken from metadata and displayed on the video as text during the whole song is playing.
When the current song finishes and a new one starts playing the previous artist/song text should be replaced with the new one etc
My question is how to properly take metadata and add it to the existing broadcast config using ffmpeg?
This is a fairly broad question and I don't have a complete solution. But I can provide a partial answer containing several commands that you can use to help implement a solution.
Update text on video on demand
See Can you insert text from a file in real time with ffmpeg streaming?
Get title & artist metadata
With ffprobe:
ffprobe -v error -show_entries format_tags=title -of default=nw=1:nk=1 input.mp3
ffprobe -v error -show_entries format_tags=artist -of default=nw=1:nk=1 input.mp3
Or combined: format_tags=title,artist (note that title will display first, then artist, regardless of order in the command).
Get duration of a song
See How to get video duration in seconds?
What you need to figure out
The hard part is knowing when to update the file referenced in textfile in drawtext filter as shown in Update text on video on demand above.
Lazy solution
Pre-make a video per song including the title and artist info. Simple Bash example:
audio=input.mp3; ffmpeg -stream_loop -1 -i video.mp4 -i "$audio" -filter_complex "[0:v]scale=1280:720:force_original_aspect_ratio=increase,crop=1280:720,setsar=1,fps=25,drawtext=text='$(ffprobe -v error -show_entries format_tags=title,artist -of default=nw=1:nk=1 $audio)':fontsize=18:fontcolor=white:x=10:y=h-th-10,format=yuv420p[v]" -map "[v]" -map 1:a -c:v libx264 -c:a aac -ac 2 -ar 44100 -g 50 -b:v 2000k -maxrate 2000k -bufsize 6000k -shortest "${audio%.*}.mp4"
Now that you already did the encoding, and everything is conformed to the same attributes for proper concatenation, you can probably just stream copy your playlist to YouTube (but I didn't test):
ffmpeg -re -f concat -i input.txt -c copy -f flv rtmp://a.rtmp.youtube.com/live2/my-key
Refer to your previous question on how to dynamically update the playlist.
References:
FFmpeg Wiki: Streaming to YouTube
Resizing videos with ffmpeg to fit into specific size
How to concatenate videos in ffmpeg with different attributes?

ffmpeg to calculate audio/visual difference between compressed and non-compressed video

I'm trying to calculate the audio + visual difference between a harshly compressed video file and one that hasn't been.
I'm using pipes because ultimately I wish this to take src from a camera stream.
I've managed to get the video results that I'm looking for, but I'm struggling with the audio.
I've added a line to invert the phase of the compressed audio, so that when they add up in the blend they should almost cancel each other out, but that doesn't happen.
ffmpeg -i input.avi -f avi -c:v libxvid -qscale:v 30 -c:a wmav1 - | \
ffmpeg -i - -f avi -af "aeval='-val(0)':c=same" - | \
ffmpeg -i input.avi -i - -filter_complex "blend=all_mode=difference" -c:v libx264 -crf 18 -f avi - | \
ffplay -
I can still hear all the audio, when what I should be hearing are solely compression artifacts. thx
To preface, I'm not sure your method would identify audio compression 'artifacts'
Your command doesn't perform any audio comparison, it only inverts a single channel. Also, the audio and video are compressed twice and the codecs the last ffmpeg command receives are the default AVI codecs of mpeg4 and mp3.
Use
ffmpeg -i input.avi -f matroska -c:v libxvid -qscale:v 30 -c:a wmav1 - |\
ffmpeg -i input.avi -i - -filter_complex "[0][1]blend=all_mode=difference;[1]aselect=gt(n\,0),asetpts=PTS-STARTPTS[1a];[0][1a]amerge,aeval=val(0)-val(1):c=mono" -c:v rawvideo -c:a pcm_s16le -f matroska - |\
ffplay -
I assume your audio is mono. If your audio has N channels, your aeval will need N expressions where the Mth expression is val(M-1)-val(N+M-1)
I also trim out the first encoded audio frame in order to mitigate encoder delay that Paul mentioned, and it seems to work here.
There might be some delay introduced with encoded audio samples. Also your command is incorrect.

How do I use ffmpeg to merge all audio streams (in a video file) into one audio channel?

I am attempting to use ffmpeg for a number of files.
The actual number of audio streams (there is usually one channel per stream) per file isn't known until I'm using ffmpeg.
The desired outcome is to somehow have ffmpeg get the count of audio channel, use the number in the command line to amerge those into one single audio channel.
The goal is to create a preview version of the original video file for use in a simple HTML5 page.
Is this possible in just one call to ffmpeg?
(Also, apologies as some parts of this problem I'm still learning about)
Edit:
Dumas stackoverflow asker here.
Yes, I've been trying multiple combinations of ffmpeg args.
To answer the other question, we have video files that have multiple streams, usually with single channels.
I'll post some cmdline examples shortly.
This cmdline example kind of does what I want; there are 8 streams, and I'm able to combine all audio into one. THe issue is having to know the number before running ffmpeg:
ffmpeg -i EXAMPLE.MOV -filter_complex "[0:v]scale=-2:720,format=yuv420p[v];[0:a]amerge=inputs=8[a]" -map "[v]" -map "[a]" -c:v libx264 -crf 23 -preset medium -c:a libmp3lame -ar 44100 -ac 2 OUTPUT.mov
You can use ffprobe to find the number of audio streams and use the output as a variable in your ffmpeg command. Bash example using wc to count the audio streams listed by ffprobe:
ffmpeg -i input.mov -filter_complex "[0:v]scale=-2:720,format=yuv420p[v];[0:a]amerge=inputs=$(ffprobe -loglevel error -select_streams a -show_entries stream=codec_type -of csv=p=0 input.mov | wc -l)[a]" -map "[v]" -map "[a]" -c:v libx264 -crf 23 -preset medium -c:a libmp3lame -ar 44100 -ac 2 output.mov
The following command should do the same thing as llogan's answer but doesn't recompress the video track and requires you to identify how many audio tracks should be merged together.
If you want to know how many audio streams are present, try:
ffprobe originalfile.mov 2>&1 | grep 'Stream #'
Once you have identified how many audio streams should be merged, use that number in the amerge=inputs=2 parameter here. This command will merge the streams into one and recompress the audio using aac compression.
ffmpeg -i originalfile.mov -c:v copy -c:a aac -b:a 160k -ac 2 -filter_complex amerge=inputs=2 output.mp4

FFmpeg concat audio with video or gif looping

I'm looking for a solution in FFmpeg to merge audio (mp3) with a short video loop, or gif.
I've already been able to generate a video from an image by joining with audio, but the video stays static frame for the audio duration, the command to make this:
ffmpeg -loop 1 -i imagem.jpg -i audio.mp3 -vcodec h264 -tune stillimage -acodec aac -b:a 64k -pix_fmt yuv420p -shortest video.mp4
I need video that has the duration of the audio, but that uses a loop of another mp4 or a gif. To keep repeating for the duration of the audio.
To do this with a video (MP4 or other format) you should use the Concatenate demuxer.
First create a text file with a list of the paths of the videos you want to concatenate. In your case it will be a list of the same video file, like the following.
# mylist.txt
file /your/path/video.mp4
file /your/path/video.mp4
file /your/path/video.mp4
The paths can be absolute or relative.
Then you need to use the concat demuxer option.
ffmpeg -f concat -safe 0 -i mylist.txt -c copy output.mp4
This will generate an mp4 with your original video looping 3 times. If your original video is 4 seconds long, then the output will be 12 seconds long. I suggest that you create a video just a bit longer than your audio track and then use the -shortest option when creating your final video.
You can add the audio within this same command like you do in your post. So, all together will look like this:
ffmpeg -f concat -safe 0 -i mylist.txt -i audio.mp3 -c:v copy -c:a copy -shortest output.mp4
In my example I do a stream copy for my output (this will work just fine and will be very fast), but you can use the codecs you want for yours (like H264 and AAC like your post).
You can find more info in the concat demuxer documentation or better yet the concat wiki.
At the moment I don't know if there's a way to do this with a gif file.

Fade out audio at the end of video using avconv with shortest option

I'm using avconv for merging an audio file with a video file. I'm using a script in which the video length will vary from file to file. So, I use -shortest flag to stop audio abruptly at the end of the video. I want to fade out audio alone for 2 seconds at the end of the video. But, I don't know the exact length of a particular video.
avconv -i inputvideo.mp4 -i inputaudio.mp3 -c:v copy -c:a aac -strict experimental -shortest videowithfadedaudio.mp4
What should I do?

Resources