How can I place a still image before the first frame of a video?

11,920

Solution 1

You can use the concat filter to do that. The exact command depends on how long you want your splash screen to be. I am pretty sure you don't want an 1-frame splash screen, which is about 1/25 to 1/30 seconds, depending on the video ;)

The Answer

First, you need to get the frame rate of the video. Try ffmpeg -i INPUT and find the tbr value. E.g.

$ ffmpeg -i a.mkv
ffmpeg version N-62860-g9173602 Copyright (c) 2000-2014 the FFmpeg developers
  built on Apr 30 2014 21:42:15 with gcc 4.8 (Ubuntu 4.8.2-19ubuntu1)
[...]
Input #0, matroska,webm, from 'a.mkv':
  Metadata:
    ENCODER         : Lavf55.37.101
  Duration: 00:00:10.08, start: 0.080000, bitrate: 23 kb/s
    Stream #0:0: Video: h264 (High 4:4:4 Predictive), yuv444p, 320x240 [SAR 1:1 DAR 4:3], 25 fps, 25 tbr, 1k tbn, 50 tbc (default)
At least one output file must be specified

In the above example, it shows 25 tbr. Remember this number.

Second, you need to concatenate the image with the video. Try this command:

ffmpeg -loop 1 -framerate FPS -t SECONDS -i IMAGE \
       -t SECONDS -f lavfi -i aevalsrc=0 \
       -i INPUTVIDEO \
       -filter_complex '[0:0] [1:0] [2:0] [2:1] concat=n=2:v=1:a=1' \
       [OPTIONS] OUTPUT

If your video doesn't have audio, try this:

ffmpeg -loop 1 -framerate FPS -t SECONDS -i IMAGE \
       -i INPUTVIDEO \
       -filter_complex '[0:0] [1:0] concat=n=2:v=1:a=0' \
       [OPTIONS] OUTPUT

FPS = tbr value got from step 1

SECONDS = duration you want the image to be shown.

IMAGE = the image name

INPUTVIDEO = the original video name

[OPTIONS] = optional encoding parameters (such as -vcodec libx264 or -b:a 160k)

OUTPUT = the output video file name

How Does This Work?

Let's split the command line I used:

-loop 1 -framerate FPS -t SECONDS -i IMAGE: this basically means: open the image, and loop over it to make it a video with SECONDS seconds with FPS frames per second. The reason you need it to have the same FPS as the input video is because the concat filter we will use later has a restriction on it.

-t SECONDS -f lavfi -i aevalsrc=0: this means: generate silence for SECONDS (0 means silence). You need silence to fill up the time for the splash image. This isn't needed if the original video doesn't have audio.

-i INPUTVIDEO: open the video itself.

-filter_complex '[0:0] [1:0] [2:0] [2:1] concat=n=2:v=1:a=1': this is the best part. You open file 0 stream 0 (the image-video), file 1 stream 0 (the silence audio), file 2 streams 0 and 1 (the real input audio and video), and concatenate them together. The options n, v, and a mean that there are 2 segments, 1 output video, and 1 output audio.

[OPTIONS] OUTPUT: this just means to encode the video to the output file name. If you are using HTML5 streaming, you'd probably want to use -c:v libx264 -crf 23 -c:a libfdk_aac (or -c:a libfaac) -b:a 128k for H.264 video and AAC audio.

Further information

Solution 2

The answer above works for me but in my case it took too much time to execute (perhaps because it re-encodes the entire video). I found another solution that's much faster. The basic idea is:

  1. Create a "video" that only has the image.
  2. Concatenate the above video with the original one, without re-encoding.

Create a video that only has the image:

ffmpeg -loop 1 -framerate 30 -i image.jpg -c:v libx264 -t 3 -pix_fmt yuv420p image.mp4

Note the -framerate 30 option. It has to be the same with the main video. Also, the image should have the same dimension with the main video. The -t 3 specifies the length of the video in seconds.

Convert the videos to MPEG-2 transport stream

According to the ffmpeg official documentation, only certain files can be concatenated using the concat protocal, this includes the MPEG-2 transport streams. And since we have 2 MP4 videos, they can be losslessly converted to MPEG-2 TS:

ffmpeg -i image.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts image.ts

and for the main video:

ffmpeg -i video.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts video.ts

Concatenate the MPEG-2 TS files

Now use the following command to concatenate the above intermediate files:

ffmpeg -i "concat:image.ts|video.ts" -c copy -bsf:a aac_adtstoasc output.mp4

Although there are 4 commands to run, combined they're still much faster then re-encoding the entire video.

Share:
11,920
Konstantin
Author by

Konstantin

Updated on June 11, 2022

Comments

  • Konstantin
    Konstantin about 2 years

    When I encode videos by FFMpeg I would like to put a jpg image before the very first video frame, because when I embed the video on a webpage with "video" html5 tag, it shows the very first picture as a splash image. Alternatively I want to encode an image to an 1 frame video and concatenate it to my encoded video. I don't want to use the "poster" property of the "video" html5 element.

  • Konstantin
    Konstantin about 10 years
    Thx, it works. But how can I pass the other filters I usually use with the -vf option: ffmpeg says -vf and -filter_complex can't be used together.
  • Timothy Gu
    Timothy Gu about 10 years
    What filters are you using? For scale, you have to first scale the image before concating it. For fps or others that change FPS, you have to use the new FPS for the -r option.
  • Konstantin
    Konstantin about 10 years
    I am using this filter chain for the video: -vf 'crop=X:Y:W:Z, mp=eq2=1.35:1.02:0.05:1.04:1.0:1.0:1.0, scale=384:-1, setsar=1'
  • mvalencaa
    mvalencaa over 7 years
    Is there a way to fade in/out the splash screen? @TimothyGu
  • klye_g
    klye_g over 6 years
    For the above commands - do the image and video have to be the same size? Or is it possible to override this with a command?
  • Timothy Gu
    Timothy Gu over 5 years
    @Damien Yes; but you could insert a scale filter specifically for the image-video to scale it up/down.
  • Alex
    Alex almost 4 years
    thanks so much for this! without any additional commands the image has to have the same dimensions, yes. amazing, thanks :)
  • Alex
    Alex almost 4 years
    @TimothyGu btw, what would the command look like to apply the audio and video codec from the input video in the output? i cant get it to work
  • porg
    porg almost 4 years
    Worked amazing! All steps itself execute in an instant, no re-encoding. But involves a lot of manual work. Could someone please put this together into a script which does all this in one pass and cleans up the auxillary ".ts" files afterwards? I imagine it like this: insertStillframeToMP4.sh -p <durationInSecs> <picFile> -v <videoFile> -o <outputFile>. The argument order of -p and -v determines whether the pic gets inserted before or after the video. The framerate is read out from the <videoFile> with some ffmpeg -i | grep parseFPSexpression.
  • craq
    craq almost 4 years
    Nice :-) this allows me to apply filters to the image and the video separately before concatenating them. Trying to do it all at once, like in Timothy's answer, gave errors which I wasn't able to fix.
  • craq
    craq almost 4 years
    btw, you could update this to use h265. Just change libx264 to libx265 in the first command, and h264_mp4toannexb to hevc_mp4toannexb everywhere else.
  • Shastry
    Shastry over 3 years
    scale and setsar are must to concatenate the image with the video. Eg: [1:v]scale=854:480, setsar=1:1[x];[x][2:a][0:v][0:a] concat=n=2:v=1:a=1 [v][a]
  • Shahriar
    Shahriar almost 3 years
    Since Edit queue is full, I write this here: - If you are on Windows, you need to delete the \ from code. - You may also need to remove quotations around the -filter_complex. example command: ffmpeg -loop 1 -framerate 23.98 -t 2 -i cover.jpg -t 2 -f lavfi -i aevalsrc=0 -i vid.mp4 -filter_complex [0:0][1:0][2:0][2:1]concat=n=2:v=1:a=1 -c:v libx264 -crf 23 final.mp4
  • Robpol86
    Robpol86 about 2 years
    Thanks for the answer. This is how I added two images before my video: fmpeg -loop 1 -framerate 29.97 -t 1.5 -i vac1.jpg -t 1.5 -f lavfi -i aevalsrc=0 -loop 1 -framerate 29.97 -t 1.5 -i vac2.jpg -t 1.5 -f lavfi -i aevalsrc=0 -i vac_ts.mkv -filter_complex '[0:v:0][1:a:0] [2:v:0][3:a:0] [4:v:0][4:a:0] concat=n=3:v=1:a=1' OPTIONS out.mkv