#!/bin/bash # convert.sh quality=30 for file in ls $1/* do tempfile="${file##*/}" filename="${tempfile%.*}" outputFile="$2/$filename.mp4" echo -e "\n\n Compress file $file" HandBrakeCLI -i $file -o $outputFile --preset="iPod" -q $quality -w 160 done
/ small-videos / original-videos FILE0001.mp4 FILE0001.MOV FILE0002.mp4 FILE0002.MOV FILE0003.mp4 FILE0003.MOV ... ...
#! /bin/sh SOURCE="/home/w32blaster/kdenlive/scripts/my-movie_001.sh.mlt" TARGET="/home/w32blaster/kdenlive/my-movie.mp4" RENDERER="/usr/bin/kdenlive_render" MELT="/usr/bin/melt" PARAMETERS="-pid:8613 $MELT hdv_1080_50i avformat - $SOURCE $TARGET properties=x264-medium g=120 crf=20 ab=160k threads=1 real_time=-1" $RENDERER $PARAMETERS
SOURCE = "/ home / w32blaster / kdenlive / scripts / my-movie_001.sh.mlt"
MLT is an open source multimedia framework, designed and developed for television broadcasting. It provides a toolkit for broadcasters, video editors, media players, transcoders, web streamers. The functionality of the plug-in based API.
<?xml version='1.0' encoding='utf-8'?> <mlt title="Anonymous Submission" version="0.9.3" root="/home/w32blaster" LC_NUMERIC="en_GB.UTF-8"> <producer in="0" out="5367" id="117_4"> <property name="resource">Videos/small/FILE0001.mp4</property> ... - </producer> <playlist id="playlist1"> <entry in="0" out="2033" producer="117_4"> <filter out="1838" id="volume"> ... ... </filter> </playlist> <tractor title="Anonymous Submission" global_feed="1" in="0" out="17018" id="maintractor"> <property name="meta.volume">1</property> <track producer="black_track"/> <track hide="video" producer="playlist1"/> <track hide="video" producer="playlist2"/> <track producer="playlist3"/> <track producer="playlist4"/> <track producer="playlist5"/> <transition id="transition0"> <property name="a_track">1</property> <property name="b_track">2</property> <property name="mlt_type">transition</property> <property name="mlt_service">mix</property> <property name="always_active">1</property> <property name="combine">1</property> <property name="internal_added">237</property> </transition> .... - , .... </tractor> </mlt>
<producer in="0" out="1327" id="65_2"> <property name="mlt_type">producer</property> <property name="length">1328</property> <property name="eof">pause</property> <property name="resource">Videos/Austria2015-small/FILE1235.mp4</property> <property name="meta.media.nb_streams">2</property> <property name="meta.media.0.stream.type">video</property> <property name="meta.media.0.stream.frame_rate">30</property> <property name="meta.media.0.stream.sample_aspect_ratio">0</property> <property name="meta.media.0.codec.width">160</property> <property name="meta.media.0.codec.height">90</property> <property name="meta.media.0.codec.frame_rate">90000</property> <property name="meta.media.0.codec.pix_fmt">yuv420p</property> <property name="meta.media.0.codec.sample_aspect_ratio">0</property> <property name="meta.media.0.codec.colorspace">709</property> <property name="meta.media.0.codec.color_trc">1</property> <property name="meta.media.0.codec.name">h264</property> <property name="meta.media.0.codec.long_name">H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10</property> <property name="meta.media.0.codec.bit_rate">52987</property> <property name="meta.media.1.stream.type">audio</property> <property name="meta.media.1.codec.sample_fmt">fltp</property> <property name="meta.media.1.codec.sample_rate">48000</property> <property name="meta.media.1.codec.channels">2</property> <property name="meta.media.1.codec.name">aac</property> <property name="meta.media.1.codec.long_name">AAC (Advanced Audio Coding)</property> <property name="meta.media.1.codec.bit_rate">163117</property> <property name="meta.attr.1.stream.language.markup">eng</property> <property name="meta.attr.major_brand.markup">mp42</property> <property name="meta.attr.minor_version.markup">512</property> <property name="meta.attr.compatible_brands.markup">isomiso2avc1</property> <property name="meta.attr.encoder.markup">HandBrake 0.10.1 2015030800</property> <property name="seekable">1</property> <property name="meta.media.sample_aspect_num">1</property> <property name="meta.media.sample_aspect_den">1</property> <property name="aspect_ratio">1</property> <property name="audio_index">1</property> <property name="video_index">0</property> <property name="mlt_service">avformat</property> <property name="meta.media.frame_rate_num">30</property> <property name="meta.media.frame_rate_den">1</property> <property name="meta.media.colorspace">709</property> <property name="meta.media.width">160</property> <property name="meta.media.height">90</property> <property name="meta.media.top_field_first">0</property> <property name="meta.media.progressive">1</property> <property name="meta.media.color_trc">1</property> </producer>
$ melt -query "consumers" --- consumers: - decklink - xgl - qglsl - sdl - sdl_audio - sdl_preview - sdl_still - cbrts - xml - sdi - jack - avformat - multi - null - gtk2_preview - blipflash - rtaudio ...
$ melt original.mlt -consumer libdv
$ melt * .MOV -consumer xml: original.mlt
<producer> <property name="resource">Videos/small/FILE1234.mp4</property> ... - </producer>
<producer> <property name="resource">Videos/original/FILE1234.MOV</property> ... - </producer>
#!/usr/bin/env python import xml.etree.ElementTree as ET import os.path import sys, getopt def main(argv): ''' The main purpose of this programm is to take the given MLT file containing producers with an compressed ("small") resources (video, images, sound) and replace them with identical producers with uncompressed ("original") resources from another MLT file Example of usage: python process.py -s ~/Videos/small.sh.mlt -p ~/Videos/original/original.mlt ''' smallVideosFile, originalVideosFile, homeDir = _extractCLArguments(argv) if (smallVideosFile == '' or originalVideosFile == ''): print "Error. Both arguments -s and -p must be provided!" sys.exit(2) print "Start MLT file processor. We will modify %s using data from %s" % (smallVideosFile, originalVideosFile) # Parse the MLT file with small resources smallTree = ET.parse(smallVideosFile) smallTreeRoot = smallTree.getroot() # and MLT file with original (big, full size) resources originalTree = ET.parse(originalVideosFile) originalTreeRoot = originalTree.getroot() # prepare maps "video file name" <==> "producer ID" mapSmallProducers = _getMapOfProducerIds(smallTreeRoot) mapOriginalProducers = _getMapOfProducerIds(originalTreeRoot) # build map "small producer ID" <==> "original producer ID", having the same video resource mapID = _mergeMaps(mapSmallProducers, mapOriginalProducers) # update 'root' value in MLT tag _updateRootTag(homeDir, smallTreeRoot) # then replace all the producers containing small videos with those with original videos _replaceAllSmallProducersWithOriginal(mapOriginalProducers, smallTreeRoot, originalTreeRoot) # print modified MLT to output file ET.ElementTree(smallTreeRoot).write(smallVideosFile, encoding='utf-8', xml_declaration=True) def _extractResourceName(fullName): ''' Extracts only the resource name from full path. For example, if the fullName is Videos/small/FILE1114.mkv this method returns FILE1114 ''' if "?" in fullName: withoutParam = fullName.split("?",1)[0] return os.path.splitext(os.path.basename(withoutParam))[0] else: return os.path.splitext(os.path.basename(fullName))[0] def _mergeMaps(mapSmallProducers, mapOriginalProducers): ''' Builds map of producer IDs: each small producer's ID should match appropriate big producer's ID, having the same resource. ''' mapID = {} for fileName, oldProducerId in mapSmallProducers.items(): if (fileName in mapOriginalProducers): mapID[oldProducerId] = mapOriginalProducers[fileName] return mapID def _getMapOfProducerIds(tree): ''' Builds the map for the given file: "resource (video file name)" <=> "producer ID". ''' arrProducers = tree.findall('producer') # Collect the map "resourse (video file name)" <==> "id of the producer" mapIdToResource = {} for producer in arrProducers: resourceType = producer.find("*[@name='mlt_service']").text isReplacementNeeded = (resourceType == "avformat" or resourceType == "framebuffer" or resourceType == "xml") if (isReplacementNeeded): resourceValue = producer.find("*[@name='resource']").text # extract only filename (without path and extension) fileName = _extractResourceName(resourceValue) mapIdToResource[fileName] = producer.attrib.get('id') else: print "[Ignored] the producer %s (resource type is %s) is ignored" % (producer.attrib.get('id'), resourceType) pass return mapIdToResource def _replaceAllSmallProducersWithOriginal(mapOriginalProducers, rootToBeModified, rootOriginal): ''' Replaces all DOM-element 'Producer' containing small resources with identical producers, containing the original resource ''' print "Replace old producer having small size with another one having original resource:" arrSmallProducers = rootToBeModified.findall('producer') for producer in arrSmallProducers: resourceType = producer.find("*[@name='mlt_service']").text if (resourceType == "avformat"): _replaceProducerAvformat(producer, mapOriginalProducers, rootToBeModified, rootOriginal) elif (resourceType == "framebuffer"): _replaceProducerFramebuffer(producer, mapOriginalProducers, rootToBeModified, rootOriginal) elif (resourceType == "xml"): _replaceProducerXml(producer, mapOriginalProducers, rootToBeModified, rootOriginal) def _replaceProducerAvformat(producer, mapOriginalProducers, rootToBeModified, rootOriginal): ''' Replace entirely one producer, containing small resource with similar producer from original tree. Resulting producer should have the same ID, but it must get all the values from original tree (URLs, codec properties and other technical information) ''' resourceValue = producer.find("*[@name='resource']").text fileName = _extractResourceName(resourceValue) if(fileName in mapOriginalProducers): # remember in, out and length from small producer attrIn = producer.attrib.get('in') attrOut = producer.attrib.get('out') length = producer.find("*[@name='length']").text producerId = producer.attrib.get('id') originalId = mapOriginalProducers[fileName] origProducer = rootOriginal.find('.//producer[@id="' + originalId + '"]') if origProducer is not None: rootToBeModified.remove(producer) # do not touch original node, deal with clone instead origProducerClone = origProducer.copy() # update in, out and length to match with small producer origProducerClone.set('in', attrIn) origProducerClone.set('out', attrOut) origProducerClone.set('id', producerId) origProducerClone.find("*[@name='length']").text = length rootToBeModified.insert(1, origProducerClone) print " - Avformat resource (id=%s) is replaced with resource (id=%s)" % (producerId, originalId) else: print " - [Ignored] Avformat resource (id=%s) was not found. File name is '%s' and found original ID is '%s'" % (producerId, fileName, originalId) def _replaceProducerFramebuffer(producer, mapOriginalProducers, rootToBeModified, rootOriginal): ''' Update given producer's resource. The resulting producer should have the same properties except resource path. Keep in mind, that we need to keep the parameter after "?" sign. ''' resourceNode = producer.find("*[@name='resource']") resourceValue = resourceNode.text producerId = producer.attrib.get('id') fileName = _extractResourceName(resourceValue) if(fileName in mapOriginalProducers): originalId = mapOriginalProducers[fileName] origProducer = rootOriginal.find('.//producer[@id="' + originalId + '"]') if origProducer is not None: originalResourcePath = origProducer.find("*[@name='resource']").text extension = resourceValue.split("?",1)[1] resourceNode.text = originalResourcePath + "?" + extension print " - Framebuffer resource (id=%s) is replaced with resource (id=%s)" % (producerId, originalId) else: print " - [Ignored] the Framebuffer resource (id=%s) is ignored!" % (producerId) def _replaceProducerXml(producer, mapOriginalProducers, rootToBeModified, rootOriginal): ''' Update given producer in case if it is XML resource. The resulting producer should have updated resource path including .mlt extension. ''' resourceNode = producer.find("*[@name='resource']") resourceValue = resourceNode.text[:-4] # trim .mlt extension producerId = producer.attrib.get('id') fileName = _extractResourceName(resourceValue) if(fileName in mapOriginalProducers): originalId = mapOriginalProducers[fileName] origProducer = rootOriginal.find('.//producer[@id="' + originalId + '"]') if origProducer is not None: originalResourcePath = origProducer.find("*[@name='resource']").text resourceNode.text = originalResourcePath + ".mlt" print " - XML resource (id=%s) is replaced with resource (id=%s)" % (producerId, originalId) def _updateRootTag(homedir, rootToBeModified): ''' In order to find all the resources, MLT uses the arggument "root" placed in tag MLT. When we work on different computers, we must also update this value in order MELT would be able to find resources. ''' if (homedir != ''): rootToBeModified.set('root', homedir) def _extractCLArguments(argv): inputfile = '' outputfile = '' homedir = '' try: opts, args = getopt.getopt(argv,"hs:p:u:",["smallfile=","producersfile=", "userhomedir="]) except getopt.GetoptError: print 'process.py -s <smallFileToBeModified> -p <producersFile>' sys.exit(2) for opt, arg in opts: if opt == '-h': print '''process.py -s <smallFile> -p <producersFile> -u <userHomeDir> where "smallFile" is a mlt file to be modified and "producersFile" is a mlt file containing the list of producers with original resources. ''' sys.exit() elif opt in ("-s", "--smallfile"): inputfile = arg elif opt in ("-p", "--producersfile"): outputfile = arg elif opt in ("-u", "--userhomedir"): homedir = arg return inputfile, outputfile, homedir if __name__ == "__main__": main(sys.argv[1:])
# python process.py -s small.mlt -p original.mlt
#!/bin/bash SMALL_RESORCES="Small" ORIGINAL_RESOURCES="Original" KDENLIVE_SCRIPT="austria-2015_001.sh" PY_SCRIPT="/home/ilya/home-workspace/mlt-producer-replacer/process.py" CURRENT_DIR="/home/ilya/Videos/MltProcessor" rm log echo -e "\n\n Step 1. Rename all the .mp4.mlt resources to .MOV.mlt" rm $ORIGINAL_RESOURCES/*.MOV.mlt for file in $ORIGINAL_RESOURCES/*.mlt; do cp "$file" "$ORIGINAL_RESOURCES/`basename $file .mp4.mlt`.MOV.mlt" done echo -e "\n\n Step 2. Generate fresh list of producers with original sources" rm $CURRENT_DIR/original.mlt cd ~ melt $CURRENT_DIR/$ORIGINAL_RESOURCES/*.{MOV,mp3} -consumer xml:original.mlt cd $CURRENT_DIR mv ~/original.mlt $CURRENT_DIR echo -e "\n\n Step 3. Update MLT and replace small videos with original ones" # process the main MLT file cp $KDENLIVE_SCRIPT.mlt $KDENLIVE_SCRIPT.mlt-BACKUP python $PY_SCRIPT -s $KDENLIVE_SCRIPT.mlt -p $CURRENT_DIR/original.mlt -u $HOME echo -e "\n\n Step 4. Update additional MLT files" # process other MLT resources, that are producers with type=xml for i in $ORIGINAL_RESOURCES/*.MOV.mlt; do python $PY_SCRIPT -s $i -p $CURRENT_DIR/original.mlt -u $HOME done echo -e "\n\n Step 5. Run rendering" ./$KDENLIVE_SCRIPT echo -e "\n\n Step 6. Make it smaller" HandBrakeCLI -i austria.mp4 -o austria.small.m4v --preset="Universal" echo "FINISHED" >> log
<producer id="12_1" in="0" out="375"> <property name="mlt_type">producer</property> <property name="resource">Videos/MltProcessor/Original/IMG_3764.MOV.mlt</property> <property name="mlt_service">xml</property> .... </producer>
#!/bin/bash SERVER_HOSTNAME="10.20.30.40" USERNAME="ilya" REMOTE_PROJECT_DIR="/home/ilya/Videos/MltProcessor" REMOTE_ORIGINAL_DIR="/home/ilya/Videos/MltProcessor/Original" LOCAL_MLT="/home/w32blaster/kdenlive/scripts/austria-2015_001.sh.mlt" LOCAL_SOURCES_DIR="/home/w32blaster/Videos/Austria2015-small" # 1. Upload main MLT file scp $LOCAL_MLT $USERNAME@$SERVER_HOSTNAME:$REMOTE_PROJECT_DIR # 2. Upload all others MLT files, that represents producer sources scp $LOCAL_SOURCES_DIR/*.mlt $USERNAME@$SERVER_HOSTNAME:$REMOTE_ORIGINAL_DIR # 3. Execute rendering on the remote server ssh $USERNAME@$SERVER_HOSTNAME "$REMOVE_PROJECT_DIR/run.sh"
Source: https://habr.com/ru/post/260837/
All Articles