2020 Apr. 18.
Videofile.rb
定義関数
# ファイル名からEPGREC録画ファイル名形式(epgrec-stem)部分を取り出す
# EPGREC-type-filename: ^20\d{12}_20\d{12}(GR|BS)CHAN.EXT
#
# epgrec_stem
# 20170923012300_20170923022200GR22.ts -> 20170923012300_20170923022200GR22
# 20170923210000_20170923215900BS103.ts -> 20170923210000_20170923215900BS103
def get_epgrec_stem
# get array of VideoCodecs in Videofile
def getArrayVideoCodecs
# get array of AudioCodecs in Videofile
def getArrayAudioCodecs
# get array of program-numbers in Videofile
def getArrayProgramNumbers
# convert raw-ts-file to split-ts-file
def convertRawTs2splitTsFile
# convert ts-file to H.264-file
def convertTs2H264File
# get array of same basename filenames
def getArraySameBasenameFileNames
# get array of filenames of same-epgrec-stem in the directory
def getArraySameEpgrecStemFileNamesInDir
# get array of filenames of same-epgrec-stem in the directory recursively
def getArraySameEpgrecStemFileNamesInDirRecursive
# delete redundant files from same epgrec-stem-files in derectory.
def deleteRedundantEpgrecStemFilesInDir(recursive)
# get epgrec-do-record-file-mode-number of arguement
def getEpgrecDoRecordMode
# get array of epgrec-do-record-mode and filename of smallest-stem-file
def getEpgrecDoRcordIdFilePathSmallestStemFile
Videofile.rb
# coding: utf-8 # ruby2.0 # 2020 Apr. 18. # 2013 Oct. 27. # Ryuichi Hashimoto # This is utility for video-file. # If filename is EPGREC-type, more functions work. # Limited functions works with non EPGREC-type filename. # EPGREC-type-filename: ^20\d{12}_20\d{12}(GR|BS)CHAN.EXT # 20170923012300_20170923022200GR22.ts # 20170923210000_20170923215900BS103.ts require 'open3' class Videofile FFMPEG="ffmpeg" # FFMPEG="avconv" private_class_method :new def self.init(fname) if File.file?(fname) new(fname) else nil end end def initialize(fname) @filename = fname @filedir = File.dirname(@filename) @filebasename = File.basename(@filename, '.*') end # get array of VideoCodecs in Videofile def getArrayVideoCodecs command = "#{FFMPEG} -i #{@filename}" stdout = '' stdout, stderr, status = Open3.capture3(command) outlines = stderr.encode("UTF-16BE", :invalid => :replace, :undef => :replace, :replace => '?').encode("UTF-8").split("\n") codec = Array.new outlines.each do | line | codec << line if line.index(": Video:") end return codec end # get array of AudioCodecs in Videofile def getArrayAudioCodecs command = "#{FFMPEG} -i #{@filename}" stdout = '' stdout, stderr, status = Open3.capture3(command) outlines = stderr.encode("UTF-16BE", :invalid => :replace, :undef => :replace, :replace => '?').encode("UTF-8").split("\n") codec = Array.new outlines.each do | line | codec << line if line.index(": Audio:") end return codec end # get array of program-numbers in Videofile def getArrayProgramNumbers command = "#{FFMPEG} -i #{@filename}" stdout = '' stdout, stderr, status = Open3.capture3(command) outlines = stderr.encode("UTF-16BE", :invalid => :replace, :undef => :replace, :replace => '?').encode("UTF-8").split("\n") programLines = Array.new program = Array.new outlines.each do | line | programLines << line if line.index("Program ") end # in case that string 'Program' is not contained in outlines if programLines.length < 1 then # if ": Video:" or ": Audio:" is contained in outlines, a stream exists without program-number outlines.each do | line | if (line.index(": Video:") || line.index(": Audio:")) program << nil return program end end else # in case that string 'Program' is contained in outlines programLines.each do | programWord | programWord.match(/[0-9]+/) program << $& end end return program end # convert raw-ts-file to split-ts-file def convertRawTs2splitTsFile outFileName = @filename + "_part.ts" videoPrograms = getArrayProgramNumbers if videoPrograms.length > 1 then sid = videoPrograms.shift system("tssplitter_lite #{@filename} #{outFileName} #{sid}") return outFileName elsif videoPrograms.length == 1 then File.rename(@filename, outFileName) return outFileName else return nil end end # convert ts-file to H.264-file def convertTs2H264File if !(splitTsFileName = convertRawTs2splitTsFile) then return nil end h264FileName = splitTsFileName + ".mp4" thread = `/usr/bin/getconf _NPROCESSORS_ONLN`.chomp ts2H264 = "#{FFMPEG} -y -i #{splitTsFileName} -f mp4 -c:v libx264 -preset slow -r 30000/1001 -aspect 16:9 -s 800x450 -bufsize 20000k -maxrate 25000k -acodec copy -threads #{thread} #{h264FileName} 2> /dev/null" system(ts2H264) return $? end # get array of same basename filenames def getArraySameBasenameFileNames fileDir = File.dirname(@filename) fileBasename = File.basename(@filename, '.*') return Dir.glob(fileDir + '/' + fileBasename + '*') end # get array of filenames of same-epgrec-stem in the directory def getArraySameEpgrecStemFileNamesInDir stm = self.get_epgrec_stem if stm then Dir.glob(@filedir + '/' + self.get_epgrec_stem + '*') else return nil end end # get array of filenames of same-epgrec-stem in the directory recursively def getArraySameEpgrecStemFileNamesInDirRecursive stm = self.get_epgrec_stem if stm then Dir.glob(@filedir + '/**/*/' + self.get_epgrec_stem + '*') else return nil end end # delete redundant files from same epgrec-stem-files in derectory. # return a filename which is not redundant. def deleteRedundantEpgrecStemFilesInDir(recursive) # get same-stem-files if recursive then stemFiles = getArraySameEpgrecStemFileNamesInDirRecursive else stemFiles = getArraySameEpgrecStemFileNamesInDir end if !stemFiles then return nil end # get each file-size & get array of filename & filesize stemFilesPathSize = Array.new() stemFiles.each do | stemFilePath | # push an array[path, size] to the array(stemFilesPathSize[]) stemFilesPathSize.push [stemFilePath, File.stat(stemFilePath).size] end # sort array of filename & filesize by size stemFilesPathSize.sort!{|a,b|(a[1]<=>b[1])} # check each file has video-stream stemFilesCounter = 0 while stemFilesCounter < stemFilesPathSize.length do video = Videofile.init(stemFilesPathSize[stemFilesCounter][0]) # If this file has video-stream and the file-size is larger than 1MB, # delete other stem-files # The file less than 1MB might be broken. if ( video.getArrayVideoCodecs.length > 0 ) then deleteFilesNumber = stemFilesPathSize.length - stemFilesCounter -1 deleteFilesCounter = 0 while deleteFilesCounter < deleteFilesNumber do # delete the same-stem-file if its modified time is more than 10 seconds ago and the size of the least size file is larger than 1MB. if ( (Time.now - File.mtime(stemFilesPathSize[stemFilesCounter + deleteFilesCounter + 1][0])) > 10 ) && ( File.size(stemFilesPathSize[stemFilesCounter][0]) > 1000000 ) then File.delete(stemFilesPathSize[stemFilesCounter + deleteFilesCounter + 1][0]) end deleteFilesCounter += 1 end # return a filename which is not redundant. return stemFilesPathSize[stemFilesCounter][0] # delete no-video-stream file else File.delete(stemFilesPathSize[stemFilesCounter][0]) end stemFilesCounter += 1 end return nil end # get epgrec-do-record-file-mode-number of arguement def getEpgrecDoRecordMode mpeg2StreamCount = 0 videoCodecs = getArrayVideoCodecs if videoCodecs.length < 1 then return nil elsif videoCodecs.length < 2 then videoCodec = videoCodecs[0].match(/: Video: \S+ /) return 1 if videoCodec.index('mpeg2video') return 2 if videoCodec.index('h264') audioCodecs = getArrayAudioCodecs audioCodec = audioCodecs[0].match(/: Audio: \S+ /) return 3 if audioCodec.index('mp3') else videoCodec = videoCodecs[0].match(/: Video: \S+ /) return 0 end end # get array of epgrec-do-record-mode and filename of smallest-stem-file def getEpgrecDoRcordIdFilePathSmallestStemFile stemFilePath = deleteRedundantEpgrecStemFiles stemVideo = Videofile.init(stemFilePath) doRecordMode = stemVideo.getEpgrecDoRecordMode stemIdFileName = Array.new() stemIdFileName.push [doRecordMode, stemFilePath] return stemIdFileName end def get_epgrec_stem fileBasenameWithPiriod = @filebasename + "." fileStemWithPeriod = fileBasenameWithPiriod.match(/^20\d{12}_20\d{12}(GR|BS).*?\./).to_a[0] if fileStemWithPeriod != nil then return File.basename(fileStemWithPeriod, '.*') else return nil end end end