├── LICENSE.md ├── README.md ├── ffmpeg-appender-test.py ├── ffmpeg-appender-test.sh ├── ffmpeg-image-sequencer.sh ├── images ├── earth.jpg ├── picture1.jpg ├── picture2.jpg └── picture3.jpg └── video_space.avi /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Bertrand Martel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FFmpeg image video sequencer 2 | 3 | Create videos from image and append images to existing videos using ffmpeg 4 | 5 | Project is available in Linux Bash script and in Python script 6 | 7 | ffmpeg 2.1+ is required 8 | 9 |

Bash

10 | 11 | create video from image or append image to video : 12 | 13 | `./ffmpeg-image-sequencer.sh -i file1:file2:file3 -f ` 14 | 15 | Video must be encoded in mjpeg or mpeg4 16 | path_to_video_output may already exists if that is the case the image are appended to it (if the video format is correct) 17 | 18 |

Exemple

19 | 20 | ``./ffmpeg-image-sequencer.sh -i ./image/picture1.jpg`` 21 | 22 | This will create a MPEG4 video showing picutre1.jpg during 1 second 23 | 24 | 25 | ``./ffmpeg-image-sequencer.sh -i ./image/picture2.jpg -f video2.mpeg`` 26 | 27 | This will append picture2.jpg to video2.mpeg 28 | 29 |

Testing script

30 | 31 | `./ffmpeg-appender_test.sh` 32 | 33 |

Python

34 | 35 | Testing script : `python ffmpeg-encoder_test.py` 36 | 37 |

Testing script : what does it do ?

38 | 39 | * download images from WEB 40 | * create a video from image if no video existing 41 | * create a video from image and append to existing video 42 | 43 | The output is a video created in output directory which contains all 4 images getting from websites and with 1 second of frame rate. 44 | 45 |

How does it work ?

46 | 47 | You can use ffmpeg create your video and append JPG image to it. 48 | 49 | To create the video from an existing image : 50 | 51 | ``` 52 | ffmpeg -f image2 -pattern_type sequence -start_number 0 -r 1 -i $TEMPORARY_FILE_NAME.jpg -s 1920x1080 $TEMPORARY_FILE_NAME.avi 53 | ``` 54 | 55 | * `-f image2` is Image file demuxer format 56 | * `pattern_type sequence` is for sequential pattern 57 | * `start_number` is the index of the sequence 58 | * `-r 1` is the frame rate, I put it to 1 frame/s to have time to see each picture 59 | * `-s 1920x1080` set the frame size 60 | 61 | To append an image to an existing a video, you will have to convert this image to a temporary video first, and then concat your former video to this newly created video. To concat videos : 62 | 63 | ``` 64 | ffmpeg -i "concat:$OUTPUT_FILE_NAME.avi|$TEMPORARY_FILE_NAME.avi" -c copy $TEMPORARY_FILE_NAME_VIDEO.avi 65 | ``` 66 | 67 | * `-i "concat:$OUTPUT_FILE_NAME.avi|$TEMPORARY_FILE_NAME.avi"` take videos to concat, you can add more if you like between | 68 | * `-c copy` will copy all video streams without reencoding 69 | 70 | You can find more information on ffmpg here https://ffmpeg.org/ffmpeg.html 71 | 72 |
-------------------------------------------------------------------------------- /ffmpeg-appender-test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | ##################################################################################### 3 | ##################################################################################### 4 | # 5 | # title : ffmpeg-appender-test.py 6 | # authors : Bertrand Martel 7 | # copyrights : Copyright (c) 2015 Bertrand Martel 8 | # license : The MIT License (MIT) 9 | # date : 16/08/2015 10 | # description : create video if not exist and append a series of image to this video taken from WEB 11 | # usage : python ffmpeg-appender-test.py 12 | # 13 | ##################################################################################### 14 | ##################################################################################### 15 | 16 | import sys, getopt, os, subprocess 17 | 18 | def main(argv): 19 | 20 | output_file_name = "video_space" 21 | temporary_file_name = "temp_space" 22 | temporary_file_name_video = "temp_video" 23 | 24 | picture_array = [ "https://upload.wikimedia.org/wikipedia/commons/4/4e/Anttlers101.jpg", \ 25 | "https://upload.wikimedia.org/wikipedia/commons/3/3b/NASA-SpiralGalaxyM101-20140505.jpg", \ 26 | "https://upload.wikimedia.org/wikipedia/commons/b/b0/Supernova_in_M101_2011-08-25.jpg", \ 27 | "http://1.1.1.5/bmi/images.nationalgeographic.com/wpf/media-live/photos/000/061/cache/earth-full-view_6125_990x742.jpg" ] 28 | 29 | this_dir = os.path.dirname(os.path.abspath(__file__)) 30 | os.chdir(this_dir) 31 | 32 | output_file_path = ''.join([this_dir , "/",output_file_name,".avi"]) 33 | temporary_file_path_avi = ''.join([this_dir,"/",temporary_file_name,".avi"]) 34 | temporary_file_name_jpg = ''.join([this_dir,"/",temporary_file_name,".jpg"]) 35 | temporary_file_name_video = ''.join([this_dir,"/",temporary_file_name_video,".avi"]) 36 | 37 | #remove files 38 | 39 | try: 40 | os.remove(output_file_path) 41 | except OSError: 42 | pass 43 | 44 | try: 45 | os.remove(temporary_file_path_avi) 46 | except OSError: 47 | pass 48 | 49 | try: 50 | os.remove(temporary_file_name_jpg) 51 | except OSError: 52 | pass 53 | 54 | try: 55 | os.remove(temporary_file_name_video) 56 | except OSError: 57 | pass 58 | 59 | for picture in picture_array: 60 | 61 | subprocess.call(["wget", picture, "-O", temporary_file_name_jpg]) 62 | 63 | subprocess.call(["ffmpeg -nostdin -v verbose -f image2 -pattern_type sequence -start_number 0 -r 1 -i " + temporary_file_name_jpg + " -s 1920x1080 " + temporary_file_path_avi],shell=True) 64 | 65 | try: 66 | os.remove(temporary_file_name_jpg) 67 | except OSError: 68 | pass 69 | 70 | if os.path.exists(output_file_path): 71 | 72 | # concat this video and former video 73 | subprocess.call(['cd ' + this_dir + ' | ffmpeg -nostdin -v verbose -i "concat:' + output_file_name + '.avi|' + temporary_file_name + '.avi" -c copy ' + temporary_file_name_video],shell=True) 74 | 75 | try: 76 | os.remove(temporary_file_path_avi) 77 | except OSError: 78 | pass 79 | 80 | try: 81 | os.remove(output_file_path) 82 | except OSError: 83 | pass 84 | 85 | os.rename(temporary_file_name_video, output_file_path) 86 | 87 | else: 88 | os.rename(temporary_file_path_avi, output_file_path) 89 | 90 | if __name__ == "__main__": 91 | main(sys.argv[1:]) 92 | 93 | __author__ = "Bertrand Martel" 94 | __copyright__ = "Copyright 2015, Bertrand Martel" 95 | __credits__ = ["Bertrand Martel"] 96 | __license__ = "MIT" 97 | __version__ = "1.0.0" 98 | __maintainer__ = "Bertrand Martel" 99 | __email__ = "bmartel.fr@gmail.com" 100 | __status__ = "POC" -------------------------------------------------------------------------------- /ffmpeg-appender-test.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | ##################################################################################### 3 | ##################################################################################### 4 | # 5 | # title : ffmpeg-appender-test.sh 6 | # authors : Bertrand Martel 7 | # copyrights : Copyright (c) 2015 Bertrand Martel 8 | # license : The MIT License (MIT) 9 | # date : 16/08/2015 10 | # description : create a MPEG4 video from a sequence of images taken from the web 11 | # usage : ./ffmpeg-appender-test.sh 12 | # 13 | ##################################################################################### 14 | ##################################################################################### 15 | 16 | #set workign directory 17 | WORKING_DIR=./ 18 | 19 | OUTPUT_FILE_NAME="video_space" 20 | OUTPUT_FILE_PATH="$WORKING_DIR/$OUTPUT_FILE_NAME.avi" 21 | TEMPORARY_FILE_NAME="temp_space" 22 | TEMPORARY_FILE_NAME_VIDEO="temp_video" 23 | 24 | #set a list of picture to download 25 | declare -a PICTURE_ARRAY=("https://upload.wikimedia.org/wikipedia/commons/4/4e/Anttlers101.jpg" \ 26 | "https://upload.wikimedia.org/wikipedia/commons/3/3b/NASA-SpiralGalaxyM101-20140505.jpg" \ 27 | "https://upload.wikimedia.org/wikipedia/commons/b/b0/Supernova_in_M101_2011-08-25.jpg" \ 28 | "https://upload.wikimedia.org/wikipedia/commons/9/97/The_Earth_seen_from_Apollo_17.jpg") 29 | 30 | #change directory for ffmpeg processing 31 | cd $WORKING_DIR 32 | 33 | #remove temporary and output files if existing 34 | rm -rf $OUTPUT_FILE_PATH 35 | rm -rf $TEMPORARY_FILE_NAME.avi 36 | rm -rf $TEMPORARY_FILE_NAME.jpg 37 | rm -rf $TEMPORARY_FILE_NAME_VIDEO.avi 38 | 39 | for i in {0..3} 40 | { 41 | #get image from WEB path 42 | wget ${PICTURE_ARRAY[i]} -O $TEMPORARY_FILE_NAME.jpg 43 | 44 | #create a video from the image 45 | ffmpeg -nostdin -v verbose -f image2 -pattern_type sequence -start_number 0 -r 1 -i $TEMPORARY_FILE_NAME.jpg -s 1920x1080 $TEMPORARY_FILE_NAME.avi 46 | 47 | #remove jpg file we have just downloaded 48 | rm $TEMPORARY_FILE_NAME.jpg 49 | 50 | if [ -f $OUTPUT_FILE_PATH ]; 51 | then 52 | # concat this video and former video into a new one 53 | ffmpeg -nostdin -v verbose -i "concat:$OUTPUT_FILE_NAME.avi|$TEMPORARY_FILE_NAME.avi" -c copy $TEMPORARY_FILE_NAME_VIDEO.avi 54 | 55 | #remove the former output and the video previously created 56 | rm $TEMPORARY_FILE_NAME.avi 57 | rm $OUTPUT_FILE_NAME.avi 58 | 59 | #rename to the output 60 | mv $TEMPORARY_FILE_NAME_VIDEO.avi $OUTPUT_FILE_NAME.avi 61 | else 62 | mv $TEMPORARY_FILE_NAME.avi $OUTPUT_FILE_NAME.avi 63 | fi 64 | } 65 | -------------------------------------------------------------------------------- /ffmpeg-image-sequencer.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | ##################################################################################### 3 | ##################################################################################### 4 | # 5 | # title : ffmpeg-image-sequencer.sh 6 | # authors : Bertrand Martel 7 | # copyrights : Copyright (c) 2015 Bertrand Martel 8 | # license : The MIT License (MIT) 9 | # date : 16/08/2015 10 | # description : create a MPEG4 video if not exist and append one or a list of JPG images to this video 11 | # usage : ./ffmpeg-image-sequencer.sh -i file1:file2:file3 -f 12 | # 13 | ##################################################################################### 14 | ##################################################################################### 15 | 16 | #default path is in current directory 17 | 18 | # A POSIX variable 19 | OPTIND=1 # Reset in case getopts has been used previously in the shell. 20 | 21 | # Initialize our own variables: 22 | output_file="" 23 | input_file="" 24 | 25 | verbose=0 26 | 27 | while getopts "h?i:f:" opt; do 28 | case "$opt" in 29 | h|\?) 30 | echo "Usage : ffmpeg-image-sequencer.sh ::.....:" 31 | exit 0 32 | ;; 33 | i) input_file=$OPTARG 34 | ;; 35 | f) output_file=$OPTARG 36 | ;; 37 | esac 38 | done 39 | 40 | shift $((OPTIND-1)) 41 | 42 | [ "$1" = "--" ] && shift 43 | 44 | echo "Processing list of files : $input_file" 45 | echo "Processing to output file : $output_file" 46 | 47 | if [ -z $input_file ]; then 48 | echo "Usage : ffmpeg-image-sequencer.sh ::.....:" 49 | exit 0 50 | fi 51 | 52 | FILE_LIST="$input_file" 53 | VIDEO_INIT="" 54 | 55 | #set workign directory 56 | WORKING_DIR=./ 57 | 58 | OUTPUT_FILE_NAME="video_output" 59 | OUTPUT_FILE_PATH="$WORKING_DIR/$OUTPUT_FILE_NAME.avi" 60 | TEMPORARY_FILE_NAME="temp" 61 | TEMPORARY_FILE_NAME_VIDEO="temp_video" 62 | 63 | #change directory for ffmpeg processing 64 | cd $WORKING_DIR 65 | 66 | if [ "$output_file" != "$OUTPUT_FILE_NAME.avi" ]; then 67 | rm -rf $OUTPUT_FILE_NAME.avi 68 | fi 69 | 70 | if [ -f $output_file ]; then 71 | cp $output_file $WORKING_DIR/$OUTPUT_FILE_NAME.avi 72 | fi 73 | 74 | IFS=':' read -ra ADDR <<< "$FILE_LIST" 75 | 76 | for i in "${ADDR[@]}"; do 77 | 78 | #create a video from the image 79 | ffmpeg -nostdin -v verbose -f image2 -pattern_type sequence -start_number 0 -r 1 -i $i -s 1920x1080 $TEMPORARY_FILE_NAME.avi 80 | 81 | if [ -f $OUTPUT_FILE_PATH ]; 82 | then 83 | # concat this video and former video into a new one 84 | ffmpeg -nostdin -v verbose -i "concat:$OUTPUT_FILE_NAME.avi|$TEMPORARY_FILE_NAME.avi" -c copy $TEMPORARY_FILE_NAME_VIDEO.avi 85 | 86 | #remove the former output and the video previously created 87 | rm $TEMPORARY_FILE_NAME.avi 88 | rm $OUTPUT_FILE_NAME.avi 89 | 90 | #rename to the output 91 | mv $TEMPORARY_FILE_NAME_VIDEO.avi $OUTPUT_FILE_NAME.avi 92 | else 93 | mv $TEMPORARY_FILE_NAME.avi $OUTPUT_FILE_NAME.avi 94 | fi 95 | 96 | done -------------------------------------------------------------------------------- /images/earth.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bertrandmartel/ffmpeg-image-sequencer/42145bc9079673e388e982bafe768d50cd5d8b13/images/earth.jpg -------------------------------------------------------------------------------- /images/picture1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bertrandmartel/ffmpeg-image-sequencer/42145bc9079673e388e982bafe768d50cd5d8b13/images/picture1.jpg -------------------------------------------------------------------------------- /images/picture2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bertrandmartel/ffmpeg-image-sequencer/42145bc9079673e388e982bafe768d50cd5d8b13/images/picture2.jpg -------------------------------------------------------------------------------- /images/picture3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bertrandmartel/ffmpeg-image-sequencer/42145bc9079673e388e982bafe768d50cd5d8b13/images/picture3.jpg -------------------------------------------------------------------------------- /video_space.avi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bertrandmartel/ffmpeg-image-sequencer/42145bc9079673e388e982bafe768d50cd5d8b13/video_space.avi --------------------------------------------------------------------------------